├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── field-path-completion.png ├── go-to-model-declaration.png ├── js-completion.png ├── model-member-completion-1.png ├── model-member-completion-2.png ├── model-member-completion-3.png ├── model-name-completion.png ├── project-structure.png ├── xml-completion.png └── xml-id-completion.png ├── settings.gradle └── src └── main ├── java └── dev │ └── ngocta │ └── pycharm │ └── odoo │ ├── OdooFilePathReferenceContributor.java │ ├── OdooFilePathReferenceProvider.java │ ├── OdooFileReferenceSet.java │ ├── OdooHtmlLanguageInjector.java │ ├── OdooNames.java │ ├── OdooPsiElementCellRenderer.java │ ├── OdooScopeOptimizer.java │ ├── OdooUtils.java │ ├── css │ └── OdooCssClassReference.java │ ├── csv │ ├── OdooCsvField.java │ ├── OdooCsvFieldManipulator.java │ ├── OdooCsvHighlightUsagesHandler.java │ ├── OdooCsvHighlightUsagesHandlerFactory.java │ ├── OdooCsvParserDefinition.java │ ├── OdooCsvRecord.java │ ├── OdooCsvUnresolvedReferencesInspection.java │ └── OdooCsvUtils.java │ ├── data │ ├── OdooExternalIdFindUsagesProvider.java │ ├── OdooExternalIdGotoContributor.java │ ├── OdooExternalIdIndex.java │ ├── OdooExternalIdReference.java │ ├── OdooExternalIdReferenceContributor.java │ ├── OdooExternalIdReferenceProvider.java │ ├── OdooRecord.java │ ├── OdooRecordDocumentProvider.java │ ├── OdooRecordElement.java │ ├── OdooRecordExtraInfo.java │ ├── OdooRecordFieldInfo.java │ ├── OdooRecordFieldInfoExternalizer.java │ ├── OdooRecordModelInfo.java │ ├── OdooRecordModelInfoExternalizer.java │ ├── OdooRecordViewInfo.java │ ├── OdooRecordViewInfoExternalizer.java │ └── filter │ │ ├── OdooRecordFilter.java │ │ ├── OdooRecordFilters.java │ │ ├── OdooRecordModelFilter.java │ │ └── OdooRecordViewModelFilter.java │ ├── javascript │ ├── OdooJQueryCSSLanguageInjector.java │ ├── OdooJSDialectSpecificHandlersFactory.java │ ├── OdooJSElementScopeProvider.java │ ├── OdooJSFieldWidget.java │ ├── OdooJSFieldWidgetDescriptionProvider.java │ ├── OdooJSFieldWidgetFindUsagesProvider.java │ ├── OdooJSFieldWidgetGotoContributor.java │ ├── OdooJSFieldWidgetIndex.java │ ├── OdooJSFieldWidgetReference.java │ ├── OdooJSFieldWidgetReferenceProvider.java │ ├── OdooJSLanguageInjector.java │ ├── OdooJSModule.java │ ├── OdooJSModuleDescriptionProvider.java │ ├── OdooJSModuleFindUsagesProvider.java │ ├── OdooJSModuleIndex.java │ ├── OdooJSModuleReference.java │ ├── OdooJSModuleReferenceContributor.java │ ├── OdooJSModuleRenameInputValidator.java │ ├── OdooJSReferenceContributor.java │ ├── OdooJSTargetElementEvaluator.java │ ├── OdooJSTypeEvaluator.java │ └── OdooJSUtils.java │ ├── python │ ├── OdooCanonicalPathProvider.java │ ├── OdooEnvironmentItemProvider.java │ ├── OdooFunctionParamBooleanTypeProvider.java │ ├── OdooHttpRouteFunctionParamTypeProvider.java │ ├── OdooImportCandidateProvider.java │ ├── OdooInitHookFunctionParamTypeProvider.java │ ├── OdooMigrateFunctionParamTypeProvider.java │ ├── OdooPyDocStringTypeProvider.java │ ├── OdooPyFunctionSelfParamNameMacro.java │ ├── OdooPyInspectionExtension.java │ ├── OdooPyRunnableScriptFilter.java │ ├── OdooPySuperFunctionParamMacro.java │ ├── OdooPyTranslationStringFormatInspection.java │ ├── OdooPyUtils.java │ ├── OdooPythonLanguageInjector.java │ ├── OdooPythonParserDefinition.java │ ├── OdooRefCallTypeProvider.java │ ├── OdooRegistryItemProvider.java │ ├── OdooSelfTypeProvider.java │ ├── OdooSuperMethodCompletionContributor.java │ ├── OdooTemplateVariableTypeProvider.java │ ├── OdooTestClassMembersProvider.java │ ├── OdooTranslationStringTypeProvider.java │ ├── model │ │ ├── OdooFieldIndex.java │ │ ├── OdooFieldInfo.java │ │ ├── OdooFieldPathReferences.java │ │ ├── OdooFieldReference.java │ │ ├── OdooFieldReferenceContributor.java │ │ ├── OdooFieldReferenceProvider.java │ │ ├── OdooFieldTypeProvider.java │ │ ├── OdooLambdaTypeProvider.java │ │ ├── OdooMappedTypeProvider.java │ │ ├── OdooModelClass.java │ │ ├── OdooModelClassInheritorsSearch.java │ │ ├── OdooModelClassSuperType.java │ │ ├── OdooModelClassType.java │ │ ├── OdooModelFunctionPublicReference.java │ │ ├── OdooModelFunctionReference.java │ │ ├── OdooModelFunctionReferenceContributor.java │ │ ├── OdooModelFunctionReferenceProvider.java │ │ ├── OdooModelIndex.java │ │ ├── OdooModelInfo.java │ │ ├── OdooModelInheritIndex.java │ │ ├── OdooModelInputFilter.java │ │ ├── OdooModelLineMarkerProvider.java │ │ ├── OdooModelReference.java │ │ ├── OdooModelReferenceContributor.java │ │ ├── OdooModelReferenceProvider.java │ │ ├── OdooModelRenameInputValidator.java │ │ ├── OdooModelSelfTypeProvider.java │ │ ├── OdooModelStructureViewElement.java │ │ ├── OdooModelStructureViewFactory.java │ │ ├── OdooModelStructureViewModel.java │ │ ├── OdooModelSuperAttributeCompletionContributor.java │ │ ├── OdooModelSuperMethodsSearch.java │ │ ├── OdooModelUtils.java │ │ ├── OdooRecordInSelfTypeProvider.java │ │ ├── OdooRecordSetType.java │ │ └── OdooSearchDomainFieldReferenceResolveProvider.java │ ├── module │ │ ├── OdooAddonsImportResolver.java │ │ ├── OdooAddonsMembersProvider.java │ │ ├── OdooManifestInfo.java │ │ ├── OdooManifestReferenceContributor.java │ │ ├── OdooModule.java │ │ ├── OdooModuleDependIndex.java │ │ ├── OdooModuleDocumentationProvider.java │ │ ├── OdooModuleHookFunctionReference.java │ │ ├── OdooModuleHookFunctionReferenceProvider.java │ │ ├── OdooModuleIndex.java │ │ ├── OdooModuleReference.java │ │ ├── OdooModuleReferenceProvider.java │ │ └── OdooModuleUtils.java │ └── psi │ │ ├── OdooPyCallExpression.java │ │ ├── OdooPyNamedParameter.java │ │ ├── OdooPyQualifiedReference.java │ │ ├── OdooPyReference.java │ │ ├── OdooPyReferenceExpression.java │ │ ├── OdooPySliceExpression.java │ │ ├── OdooPyStringLiteralExpression.java │ │ ├── OdooPySubscriptionExpression.java │ │ └── OdooPyTargetExpression.java │ └── xml │ ├── OdooCssClassInXmlReferenceContributor.java │ ├── OdooCssClassInXmlReferenceProvider.java │ ├── OdooFieldWidgetInXmlReferenceContributor.java │ ├── OdooJSTemplateElement.java │ ├── OdooJSTemplateFindUsageProvider.java │ ├── OdooJSTemplateGotoContributor.java │ ├── OdooJSTemplateIndex.java │ ├── OdooJSTemplateReference.java │ ├── OdooJSTemplateReferenceContributor.java │ ├── OdooJSTemplateReferenceProvider.java │ ├── OdooViewInheritIdIndex.java │ ├── OdooXmlElementDescriptionProvider.java │ ├── OdooXmlFormatter.java │ ├── OdooXmlInspection.java │ ├── OdooXmlLineMarkerProvider.java │ ├── OdooXmlPolicy.java │ ├── OdooXmlStructureViewBuilderProvider.java │ ├── OdooXmlSuppressionProvider.java │ ├── OdooXmlUtils.java │ ├── dom │ ├── OdooDomActWindow.java │ ├── OdooDomData.java │ ├── OdooDomDataFile.java │ ├── OdooDomDataFileMetaData.java │ ├── OdooDomElement.java │ ├── OdooDomExtender.java │ ├── OdooDomField.java │ ├── OdooDomFieldAssignment.java │ ├── OdooDomJSTemplate.java │ ├── OdooDomJSTemplateFile.java │ ├── OdooDomJSTemplateFileMetaData.java │ ├── OdooDomMenuItem.java │ ├── OdooDomModelScoped.java │ ├── OdooDomModelScopedViewElement.java │ ├── OdooDomOperationContainer.java │ ├── OdooDomRecord.java │ ├── OdooDomRecordLike.java │ ├── OdooDomReport.java │ ├── OdooDomTemplate.java │ ├── OdooDomViewButton.java │ ├── OdooDomViewElement.java │ ├── OdooDomViewField.java │ ├── OdooDomViewFieldInheritLocator.java │ ├── OdooDomViewInheritLocator.java │ ├── OdooDomViewLabel.java │ ├── OdooDomViewXPath.java │ ├── OdooFieldFileValueReferenceConverter.java │ ├── OdooFieldNameReferenceConverter.java │ ├── OdooFieldRefValueReferenceConverter.java │ ├── OdooFieldValueReferenceConverter.java │ ├── OdooGroupsQualifiedReferenceConverter.java │ ├── OdooGroupsReferenceConverter.java │ ├── OdooIrActionsServerCodeTypeProvider.java │ ├── OdooModelReferenceConverter.java │ └── OdooRecordReferenceConverter.java │ └── xpath │ ├── OdooXPathFunctionProvider.java │ └── OdooXPathLanguageInjector.java └── resources ├── META-INF ├── plugin-css.xml ├── plugin-csv.xml ├── plugin-javascript.xml ├── plugin-xpath.xml ├── plugin.xml └── pluginIcon.svg ├── inspectionDescriptions ├── OdooCsvUnresolvedReferences.html ├── OdooPyTranslationStringFormat.html └── OdooXml.html └── liveTemplates └── Python.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /out 3 | /.gradle 4 | /.idea 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains source code of the old Odoo plugin versions. You can create issues and feature requests for the new versions at https://github.com/odoo-ide/pycharm-odoo/issues. In case you don't know, please read https://github.com/odoo-ide/pycharm-odoo-old/issues/50 for the reasons behind this change. 2 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.jetbrains.intellij' version '0.4.11' 4 | } 5 | 6 | group 'dev.ngocta' 7 | version '4.16' 8 | 9 | sourceCompatibility = 1.8 10 | targetCompatibility = 1.8 11 | 12 | repositories { 13 | mavenCentral() 14 | } 15 | 16 | dependencies { 17 | compile group: 'org.apache.commons', name: 'commons-csv', version: '1.7' 18 | testCompile group: 'junit', name: 'junit', version: '4.12' 19 | } 20 | 21 | // See https://github.com/JetBrains/gradle-intellij-plugin/ 22 | intellij { 23 | version 'PY-2021.1' 24 | downloadSources = true 25 | plugins = ['python', 'CSS', 'JavaScriptLanguage', 'XPathView:211.6693.44', 'net.seesharpsoft.intellij.plugins.csv:2.15.1'] 26 | } 27 | patchPluginXml { 28 | // changeNotes """ 29 | // """ 30 | } 31 | 32 | runIde { 33 | jvmArgs '-Xmx2G' 34 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 02 22:44:45 ICT 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /images/field-path-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/field-path-completion.png -------------------------------------------------------------------------------- /images/go-to-model-declaration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/go-to-model-declaration.png -------------------------------------------------------------------------------- /images/js-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/js-completion.png -------------------------------------------------------------------------------- /images/model-member-completion-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/model-member-completion-1.png -------------------------------------------------------------------------------- /images/model-member-completion-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/model-member-completion-2.png -------------------------------------------------------------------------------- /images/model-member-completion-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/model-member-completion-3.png -------------------------------------------------------------------------------- /images/model-name-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/model-name-completion.png -------------------------------------------------------------------------------- /images/project-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/project-structure.png -------------------------------------------------------------------------------- /images/xml-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/xml-completion.png -------------------------------------------------------------------------------- /images/xml-id-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odoo-ide/pycharm-odoo-old/78f639ce340d78c60eb565472f0f5b932df401b5/images/xml-id-completion.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'pycharm-odoo' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/OdooFilePathReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo; 2 | 3 | import com.intellij.patterns.XmlAttributeValuePattern; 4 | import com.intellij.patterns.XmlPatterns; 5 | import com.intellij.psi.PsiReferenceContributor; 6 | import com.intellij.psi.PsiReferenceProvider; 7 | import com.intellij.psi.PsiReferenceRegistrar; 8 | import dev.ngocta.pycharm.odoo.xml.OdooXmlUtils; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class OdooFilePathReferenceContributor extends PsiReferenceContributor { 12 | public static final XmlAttributeValuePattern SCRIPT_SRC_PATTERN = 13 | XmlPatterns.xmlAttributeValue().withParent( 14 | XmlPatterns.xmlAttribute("src").withParent( 15 | XmlPatterns.xmlTag().withLocalName("script"))).with( 16 | OdooXmlUtils.ODOO_XML_DATA_ELEMENT_PATTERN_CONDITION); 17 | 18 | public static final XmlAttributeValuePattern LINK_HREF_PATTERN = 19 | XmlPatterns.xmlAttributeValue().withParent( 20 | XmlPatterns.xmlAttribute("href").withParent( 21 | XmlPatterns.xmlTag().withLocalName("link"))).with( 22 | OdooXmlUtils.ODOO_XML_DATA_ELEMENT_PATTERN_CONDITION); 23 | 24 | public static final XmlAttributeValuePattern T_THUMBNAIL_PATTERN = 25 | XmlPatterns.xmlAttributeValue().withParent( 26 | XmlPatterns.xmlAttribute("t-thumbnail").withParent( 27 | XmlPatterns.xmlTag().withLocalName("t"))).with( 28 | OdooXmlUtils.ODOO_XML_DATA_ELEMENT_PATTERN_CONDITION); 29 | 30 | @Override 31 | public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { 32 | PsiReferenceProvider provider = new OdooFilePathReferenceProvider(); 33 | registrar.registerReferenceProvider(SCRIPT_SRC_PATTERN, provider); 34 | registrar.registerReferenceProvider(LINK_HREF_PATTERN, provider); 35 | registrar.registerReferenceProvider(T_THUMBNAIL_PATTERN, provider); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/OdooFilePathReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.PsiReferenceProvider; 6 | import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; 7 | import com.intellij.util.ProcessingContext; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class OdooFilePathReferenceProvider extends PsiReferenceProvider { 11 | @Override 12 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 13 | @NotNull ProcessingContext context) { 14 | FileReferenceSet referenceSet = new OdooFileReferenceSet(element); 15 | return referenceSet.getAllReferences(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/OdooFileReferenceSet.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo; 2 | 3 | import com.intellij.ide.BrowserUtil; 4 | import com.intellij.psi.PsiDirectory; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiFile; 7 | import com.intellij.psi.PsiFileSystemItem; 8 | import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference; 9 | import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; 10 | import com.jetbrains.python.psi.PyUtil; 11 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 12 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleIndex; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Collection; 16 | import java.util.Collections; 17 | import java.util.List; 18 | import java.util.Objects; 19 | import java.util.stream.Collectors; 20 | 21 | public class OdooFileReferenceSet extends FileReferenceSet { 22 | private final boolean myStartWithOdooModule; 23 | 24 | public OdooFileReferenceSet(@NotNull PsiElement element) { 25 | this(element, false); 26 | } 27 | 28 | public OdooFileReferenceSet(@NotNull PsiElement element, 29 | boolean startWithOdooModule) { 30 | super(element); 31 | myStartWithOdooModule = startWithOdooModule; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public Collection computeDefaultContexts() { 37 | final PsiFile file = getContainingFile(); 38 | if (file == null) { 39 | return Collections.emptyList(); 40 | } 41 | 42 | if (isAbsolutePathReference() || myStartWithOdooModule) { 43 | return PyUtil.getParameterizedCachedValue(file, null, param -> { 44 | return OdooModuleIndex.getAvailableOdooModules(getElement()).stream() 45 | .map(OdooModule::getDirectory) 46 | .map(PsiDirectory::getParent) 47 | .filter(Objects::nonNull) 48 | .collect(Collectors.toSet()); 49 | }); 50 | } 51 | 52 | if (file.getParent() != null) { 53 | return Collections.singletonList(file.getParent()); 54 | } 55 | 56 | return Collections.emptyList(); 57 | } 58 | 59 | @Override 60 | protected List reparse(String str, 61 | int startInElement) { 62 | if (BrowserUtil.isAbsoluteURL(str.trim())) { 63 | return Collections.emptyList(); 64 | } 65 | return super.reparse(str, startInElement); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/OdooHtmlLanguageInjector.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo; 2 | 3 | import com.intellij.lang.html.HTMLLanguage; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.util.TextRange; 6 | import com.intellij.patterns.PatternCondition; 7 | import com.intellij.patterns.XmlElementPattern; 8 | import com.intellij.patterns.XmlPatterns; 9 | import com.intellij.psi.ElementManipulators; 10 | import com.intellij.psi.InjectedLanguagePlaces; 11 | import com.intellij.psi.LanguageInjector; 12 | import com.intellij.psi.PsiLanguageInjectionHost; 13 | import com.intellij.psi.xml.XmlTag; 14 | import com.intellij.psi.xml.XmlText; 15 | import com.intellij.util.ProcessingContext; 16 | import com.intellij.util.xml.DomElement; 17 | import com.intellij.util.xml.DomManager; 18 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomFieldAssignment; 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | public class OdooHtmlLanguageInjector implements LanguageInjector { 22 | public static final XmlElementPattern.XmlTextPattern MAIL_TEMPLATE_BODY_PATTERN = 23 | XmlPatterns.xmlText().with(new PatternCondition("mailTemplateBody") { 24 | @Override 25 | public boolean accepts(@NotNull XmlText xmlText, 26 | ProcessingContext context) { 27 | XmlTag tag = xmlText.getParentTag(); 28 | if (tag == null) { 29 | return false; 30 | } 31 | Project project = xmlText.getProject(); 32 | DomManager domManager = DomManager.getDomManager(project); 33 | DomElement domElement = domManager.getDomElement(tag); 34 | if (domElement instanceof OdooDomFieldAssignment) { 35 | OdooDomFieldAssignment fieldAssignment = (OdooDomFieldAssignment) domElement; 36 | String field = fieldAssignment.getNameAttr().getStringValue(); 37 | String model = fieldAssignment.getModel(); 38 | return "body_html".equals(field) && OdooNames.MAIL_TEMPLATE.equals(model); 39 | } 40 | return false; 41 | } 42 | }); 43 | 44 | @Override 45 | public void getLanguagesToInject(@NotNull PsiLanguageInjectionHost host, 46 | @NotNull InjectedLanguagePlaces injectionPlacesRegistrar) { 47 | if (MAIL_TEMPLATE_BODY_PATTERN.accepts(host)) { 48 | TextRange range = ElementManipulators.getValueTextRange(host); 49 | injectionPlacesRegistrar.addPlace(HTMLLanguage.INSTANCE, range, null, null); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/OdooPsiElementCellRenderer.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo; 2 | 3 | import com.intellij.ide.util.DefaultPsiElementCellRenderer; 4 | import com.intellij.openapi.ui.popup.IPopupChooserBuilder; 5 | 6 | public class OdooPsiElementCellRenderer extends DefaultPsiElementCellRenderer { 7 | @Override 8 | public void installSpeedSearch(IPopupChooserBuilder builder) { 9 | installSpeedSearch(builder, true); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/OdooScopeOptimizer.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.search.ScopeOptimizer; 5 | import com.intellij.psi.search.SearchScope; 6 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 7 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public class OdooScopeOptimizer implements ScopeOptimizer { 12 | @Override 13 | @Nullable("is null when given optimizer can't provide a scope to restrict") 14 | public SearchScope getRestrictedUseScope(@NotNull PsiElement element) { 15 | OdooModule containingOdooModule = OdooModuleUtils.getContainingOdooModule(element); 16 | if (containingOdooModule != null) { 17 | return containingOdooModule.getOdooModuleScope(true, true); 18 | } 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/css/OdooCssClassReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.css; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 5 | import com.intellij.openapi.util.TextRange; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiElementResolveResult; 8 | import com.intellij.psi.PsiReferenceBase; 9 | import com.intellij.psi.ResolveResult; 10 | import com.intellij.psi.css.impl.stubs.index.CssClassIndex; 11 | import com.intellij.psi.css.impl.stubs.index.CssIndexUtil; 12 | import com.intellij.psi.search.GlobalSearchScope; 13 | import com.intellij.psi.stubs.StubIndex; 14 | import com.intellij.util.PlatformIcons; 15 | import com.jetbrains.python.psi.PyUtil; 16 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import java.util.LinkedList; 20 | import java.util.List; 21 | 22 | public class OdooCssClassReference extends PsiReferenceBase.Poly { 23 | public OdooCssClassReference(PsiElement psiElement, 24 | TextRange range, 25 | boolean soft) { 26 | super(psiElement, range, soft); 27 | } 28 | 29 | @Override 30 | public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) { 31 | return PyUtil.getParameterizedCachedValue(getElement(), null, param -> { 32 | List elements = new LinkedList<>(); 33 | GlobalSearchScope scope = OdooModuleUtils.getOdooModuleWithDependenciesOrSystemWideModulesScope(getElement()); 34 | CssIndexUtil.processClasses(getValue(), getElement().getProject(), scope, (s, cssSelectorSuffix) -> { 35 | elements.add(cssSelectorSuffix); 36 | return true; 37 | }); 38 | return PsiElementResolveResult.createResults(elements); 39 | }); 40 | } 41 | 42 | @Override 43 | public Object @NotNull [] getVariants() { 44 | List lookupElements = new LinkedList<>(); 45 | StubIndex.getInstance().processAllKeys(CssClassIndex.KEY, getElement().getProject(), (s) -> { 46 | lookupElements.add(LookupElementBuilder.create(s).withIcon(PlatformIcons.CLASS_ICON)); 47 | return true; 48 | }); 49 | return lookupElements.toArray(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/csv/OdooCsvFieldManipulator.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.csv; 2 | 3 | import com.intellij.openapi.util.TextRange; 4 | import com.intellij.psi.AbstractElementManipulator; 5 | import com.intellij.util.IncorrectOperationException; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class OdooCsvFieldManipulator extends AbstractElementManipulator { 10 | @Nullable 11 | @Override 12 | public OdooCsvField handleContentChange(@NotNull OdooCsvField element, 13 | @NotNull TextRange range, 14 | String newContent) throws IncorrectOperationException { 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/csv/OdooCsvHighlightUsagesHandler.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.csv; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import net.seesharpsoft.intellij.plugins.csv.highlighter.CsvHighlightUsagesHandler; 5 | import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class OdooCsvHighlightUsagesHandler extends CsvHighlightUsagesHandler { 9 | protected OdooCsvHighlightUsagesHandler(@NotNull Editor editor, @NotNull CsvFile file) { 10 | super(editor, file); 11 | } 12 | 13 | @Override 14 | public boolean highlightReferences() { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/csv/OdooCsvHighlightUsagesHandlerFactory.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.csv; 2 | 3 | import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.psi.PsiFile; 6 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 7 | import net.seesharpsoft.intellij.plugins.csv.highlighter.CsvHighlightUsagesHandlerFactory; 8 | import net.seesharpsoft.intellij.plugins.csv.psi.CsvFile; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class OdooCsvHighlightUsagesHandlerFactory extends CsvHighlightUsagesHandlerFactory { 13 | @Override 14 | @Nullable 15 | public HighlightUsagesHandlerBase createHighlightUsagesHandler(@NotNull Editor editor, 16 | @NotNull PsiFile psiFile) { 17 | if (psiFile instanceof CsvFile && OdooModuleUtils.isInOdooModule(psiFile)) { 18 | return new OdooCsvHighlightUsagesHandler(editor, (CsvFile) psiFile); 19 | } 20 | return super.createHighlightUsagesHandler(editor, psiFile); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/csv/OdooCsvParserDefinition.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.csv; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiElement; 5 | import net.seesharpsoft.intellij.plugins.csv.CsvParserDefinition; 6 | import net.seesharpsoft.intellij.plugins.csv.psi.CsvTypes; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class OdooCsvParserDefinition extends CsvParserDefinition { 10 | @Override 11 | @NotNull 12 | public PsiElement createElement(ASTNode node) { 13 | if (node.getElementType() == CsvTypes.FIELD) { 14 | return new OdooCsvField(node); 15 | } 16 | return super.createElement(node); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/csv/OdooCsvUnresolvedReferencesInspection.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.csv; 2 | 3 | import com.intellij.codeInspection.LocalInspectionTool; 4 | import com.intellij.codeInspection.ProblemsHolder; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiElementVisitor; 7 | import com.intellij.psi.PsiReference; 8 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReference; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class OdooCsvUnresolvedReferencesInspection extends LocalInspectionTool { 12 | @Override 13 | @NotNull 14 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, 15 | boolean isOnTheFly) { 16 | return new PsiElementVisitor() { 17 | @Override 18 | public void visitElement(@NotNull PsiElement element) { 19 | if (!(element instanceof OdooCsvField)) { 20 | return; 21 | } 22 | if (OdooCsvUnresolvedReferencesInspection.this.isSuppressedFor(element)) { 23 | return; 24 | } 25 | PsiReference reference = element.getReference(); 26 | if (reference instanceof OdooExternalIdReference) { 27 | if (reference.getCanonicalText().isEmpty()) { 28 | return; 29 | } 30 | if (((OdooExternalIdReference) reference).multiResolve(false).length == 0) { 31 | holder.registerProblem(reference); 32 | } 33 | } 34 | } 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/csv/OdooCsvUtils.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.csv; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.util.PairProcessor; 6 | import dev.ngocta.pycharm.odoo.data.OdooRecord; 7 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 8 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 9 | import org.apache.commons.csv.CSVFormat; 10 | import org.apache.commons.csv.CSVParser; 11 | import org.apache.commons.csv.CSVRecord; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.io.InputStream; 15 | 16 | public class OdooCsvUtils { 17 | private OdooCsvUtils() { 18 | } 19 | 20 | public static boolean isCsvFile(@NotNull VirtualFile file) { 21 | String extension = file.getExtension(); 22 | return extension != null && "csv".equals(extension.toLowerCase()); 23 | } 24 | 25 | public static void processRecordInCsvFile(@NotNull VirtualFile file, 26 | @NotNull Project project, 27 | @NotNull PairProcessor processor) { 28 | OdooModule module = OdooModuleUtils.getContainingOdooModule(file, project); 29 | if (module == null) { 30 | return; 31 | } 32 | try { 33 | InputStream inputStream = file.getInputStream(); 34 | CSVParser parser = CSVParser.parse(inputStream, file.getCharset(), CSVFormat.DEFAULT.withHeader()); 35 | if (!parser.getHeaderNames().contains("id")) { 36 | return; 37 | } 38 | String model = file.getNameWithoutExtension(); 39 | String moduleName = module.getName(); 40 | for (CSVRecord csvRecord : parser) { 41 | String id = csvRecord.get("id"); 42 | if (id != null) { 43 | OdooRecord record = new OdooRecord(id, model, moduleName, null, file); 44 | if (!processor.process(record, csvRecord)) { 45 | break; 46 | } 47 | } 48 | } 49 | } catch (Exception ignored) { 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooExternalIdFindUsagesProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import com.intellij.lang.findUsages.FindUsagesProvider; 4 | import com.intellij.pom.PomTarget; 5 | import com.intellij.pom.PomTargetPsiElement; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.util.xml.DomTarget; 8 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomRecordLike; 9 | import org.jetbrains.annotations.Nls; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class OdooExternalIdFindUsagesProvider implements FindUsagesProvider { 14 | @Override 15 | public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { 16 | if (psiElement instanceof OdooRecordElement) { 17 | return true; 18 | } 19 | if (psiElement instanceof PomTargetPsiElement) { 20 | PomTarget target = ((PomTargetPsiElement) psiElement).getTarget(); 21 | if (target instanceof DomTarget) { 22 | return ((DomTarget) target).getDomElement() instanceof OdooDomRecordLike; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | @Override 29 | @Nullable 30 | public String getHelpId(@NotNull PsiElement psiElement) { 31 | return null; 32 | } 33 | 34 | @Override 35 | @Nls 36 | @NotNull 37 | public String getType(@NotNull PsiElement element) { 38 | return ""; 39 | } 40 | 41 | @Override 42 | @Nls 43 | @NotNull 44 | public String getDescriptiveName(@NotNull PsiElement element) { 45 | return ""; 46 | } 47 | 48 | @Override 49 | @Nls 50 | @NotNull 51 | public String getNodeText(@NotNull PsiElement element, 52 | boolean useFullName) { 53 | return ""; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooExternalIdGotoContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import com.intellij.navigation.ChooseByNameContributorEx; 4 | import com.intellij.navigation.NavigationItem; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.search.GlobalSearchScope; 7 | import com.intellij.util.Processor; 8 | import com.intellij.util.indexing.FindSymbolParameters; 9 | import com.intellij.util.indexing.IdFilter; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.Collection; 14 | 15 | public class OdooExternalIdGotoContributor implements ChooseByNameContributorEx { 16 | @Override 17 | public void processNames(@NotNull Processor processor, 18 | @NotNull GlobalSearchScope scope, 19 | @Nullable IdFilter filter) { 20 | Project project = scope.getProject(); 21 | if (project != null) { 22 | Collection ids = OdooExternalIdIndex.getAllIds(project, scope); 23 | ids.forEach(processor::process); 24 | } 25 | } 26 | 27 | @Override 28 | public void processElementsWithName(@NotNull String name, 29 | @NotNull Processor processor, 30 | @NotNull FindSymbolParameters parameters) { 31 | Project project = parameters.getProject(); 32 | Collection records = OdooExternalIdIndex.findRecordsByQualifiedId(name, project, parameters.getSearchScope()); 33 | records.forEach(record -> { 34 | record.getRecordElements(project).forEach(processor::process); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooExternalIdReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import com.intellij.openapi.util.Key; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.ElementManipulators; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiReference; 8 | import com.intellij.psi.PsiReferenceProvider; 9 | import com.intellij.util.ProcessingContext; 10 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilter; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.LinkedList; 15 | import java.util.List; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | public class OdooExternalIdReferenceProvider extends PsiReferenceProvider { 20 | public static final Key FILTER = new Key<>("filter"); 21 | public static final Key ALLOW_RELATIVE = new Key<>("allowRelative"); 22 | public static final Key COMMA_SEPARATED = new Key<>("commaSeparated"); 23 | 24 | @Override 25 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 26 | @NotNull ProcessingContext context) { 27 | OdooRecordFilter filter = context.get(FILTER); 28 | Boolean allowRelative = context.get(ALLOW_RELATIVE); 29 | Boolean commaSeparated = context.get(COMMA_SEPARATED); 30 | if (Boolean.TRUE.equals(commaSeparated)) { 31 | return getCommaSeparatedReferences(element, filter, Boolean.TRUE.equals(allowRelative)); 32 | } 33 | return new PsiReference[]{new OdooExternalIdReference(element, null, filter, Boolean.TRUE.equals(allowRelative))}; 34 | } 35 | 36 | @NotNull 37 | public static PsiReference[] getCommaSeparatedReferences(@NotNull PsiElement element, 38 | @Nullable OdooRecordFilter filter, 39 | boolean allowRelative) { 40 | List result = new LinkedList<>(); 41 | TextRange textRange = ElementManipulators.getValueTextRange(element); 42 | String text = textRange.substring(element.getText()); 43 | Matcher matcher = Pattern.compile("(\\w+\\.)?\\w+").matcher(text); 44 | while (matcher.find()) { 45 | TextRange subRange = textRange.cutOut(new TextRange(matcher.start(), matcher.end())); 46 | result.add(new OdooExternalIdReference(element, subRange, filter, allowRelative)); 47 | } 48 | return result.toArray(PsiReference.EMPTY_ARRAY); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordDocumentProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import com.intellij.lang.documentation.DocumentationMarkup; 4 | import com.intellij.lang.documentation.DocumentationProvider; 5 | import com.intellij.psi.PsiElement; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public class OdooRecordDocumentProvider implements DocumentationProvider { 9 | @Nullable 10 | @Override 11 | public String generateDoc(PsiElement element, 12 | @Nullable PsiElement originalElement) { 13 | if (element instanceof OdooRecordElement) { 14 | return DocumentationMarkup.DEFINITION_START + 15 | ((OdooRecordElement) element).getPresentableText() + 16 | DocumentationMarkup.DEFINITION_END; 17 | } 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordExtraInfo.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | public abstract class OdooRecordExtraInfo { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordFieldInfo.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Objects; 6 | 7 | public class OdooRecordFieldInfo extends OdooRecordExtraInfo { 8 | private final String myFieldName; 9 | private final String myModelName; 10 | 11 | public OdooRecordFieldInfo(@NotNull String fieldName, 12 | @NotNull String modelName) { 13 | myFieldName = fieldName; 14 | myModelName = modelName; 15 | } 16 | 17 | @NotNull 18 | public String getFieldName() { 19 | return myFieldName; 20 | } 21 | 22 | @NotNull 23 | public String getModelName() { 24 | return myModelName; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) return true; 30 | if (o == null || getClass() != o.getClass()) return false; 31 | OdooRecordFieldInfo that = (OdooRecordFieldInfo) o; 32 | return Objects.equals(myFieldName, that.myFieldName) 33 | && Objects.equals(myModelName, that.myModelName); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return Objects.hash(myFieldName, myModelName); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordFieldInfoExternalizer.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import com.intellij.util.io.DataExternalizer; 4 | import dev.ngocta.pycharm.odoo.OdooUtils; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.io.DataInput; 8 | import java.io.DataOutput; 9 | import java.io.IOException; 10 | 11 | class OdooRecordFieldInfoExternalizer implements DataExternalizer { 12 | public static final OdooRecordFieldInfoExternalizer INSTANCE = new OdooRecordFieldInfoExternalizer(); 13 | 14 | @Override 15 | public void save(@NotNull DataOutput out, 16 | OdooRecordFieldInfo value) throws IOException { 17 | OdooUtils.writeNullableString(value.getFieldName(), out); 18 | OdooUtils.writeNullableString(value.getModelName(), out); 19 | } 20 | 21 | @Override 22 | public OdooRecordFieldInfo read(@NotNull DataInput in) throws IOException { 23 | String fieldName = OdooUtils.readNullableString(in); 24 | String modelName = OdooUtils.readNullableString(in); 25 | if (fieldName != null && modelName != null) { 26 | return new OdooRecordFieldInfo(fieldName, modelName); 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordModelInfo.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Objects; 6 | 7 | public class OdooRecordModelInfo extends OdooRecordExtraInfo { 8 | private final String myModelName; 9 | 10 | public OdooRecordModelInfo(@NotNull String modelName) { 11 | myModelName = modelName; 12 | } 13 | 14 | @NotNull 15 | public String getModelName() { 16 | return myModelName; 17 | } 18 | 19 | @Override 20 | public boolean equals(Object o) { 21 | if (this == o) return true; 22 | if (o == null || getClass() != o.getClass()) return false; 23 | OdooRecordModelInfo that = (OdooRecordModelInfo) o; 24 | return Objects.equals(myModelName, that.myModelName); 25 | } 26 | 27 | @Override 28 | public int hashCode() { 29 | return Objects.hash(myModelName); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordModelInfoExternalizer.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import com.intellij.util.io.DataExternalizer; 4 | import dev.ngocta.pycharm.odoo.OdooUtils; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.io.DataInput; 8 | import java.io.DataOutput; 9 | import java.io.IOException; 10 | 11 | class OdooRecordModelInfoExternalizer implements DataExternalizer { 12 | public static final OdooRecordModelInfoExternalizer INSTANCE = new OdooRecordModelInfoExternalizer(); 13 | 14 | @Override 15 | public void save(@NotNull DataOutput out, 16 | OdooRecordModelInfo value) throws IOException { 17 | OdooUtils.writeNullableString(value.getModelName(), out); 18 | } 19 | 20 | @Override 21 | public OdooRecordModelInfo read(@NotNull DataInput in) throws IOException { 22 | String modelName = OdooUtils.readNullableString(in); 23 | if (modelName != null) { 24 | return new OdooRecordModelInfo(modelName); 25 | } 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordViewInfo.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.Objects; 6 | 7 | public class OdooRecordViewInfo extends OdooRecordExtraInfo { 8 | private final String myViewType; 9 | private final String myViewModel; 10 | private final String myInheritId; 11 | 12 | public OdooRecordViewInfo(@Nullable String viewType, 13 | @Nullable String viewModel, 14 | @Nullable String inheritId) { 15 | myViewType = viewType; 16 | myViewModel = viewModel; 17 | myInheritId = inheritId; 18 | } 19 | 20 | @Nullable 21 | public String getViewType() { 22 | return myViewType; 23 | } 24 | 25 | @Nullable 26 | public String getViewModel() { 27 | return myViewModel; 28 | } 29 | 30 | @Nullable 31 | public String getInheritId() { 32 | return myInheritId; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (this == o) return true; 38 | if (o == null || getClass() != o.getClass()) return false; 39 | OdooRecordViewInfo that = (OdooRecordViewInfo) o; 40 | return Objects.equals(myViewType, that.myViewType) && 41 | Objects.equals(myViewModel, that.myViewModel) && 42 | Objects.equals(myInheritId, that.myInheritId); 43 | } 44 | 45 | @Override 46 | public int hashCode() { 47 | return Objects.hash(myViewType, myViewModel, myInheritId); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/OdooRecordViewInfoExternalizer.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data; 2 | 3 | import com.intellij.util.io.DataExternalizer; 4 | import dev.ngocta.pycharm.odoo.OdooUtils; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.io.DataInput; 8 | import java.io.DataOutput; 9 | import java.io.IOException; 10 | 11 | class OdooRecordViewInfoExternalizer implements DataExternalizer { 12 | public static final OdooRecordViewInfoExternalizer INSTANCE = new OdooRecordViewInfoExternalizer(); 13 | 14 | @Override 15 | public void save(@NotNull DataOutput out, 16 | OdooRecordViewInfo value) throws IOException { 17 | OdooUtils.writeNullableString(value.getViewType(), out); 18 | OdooUtils.writeNullableString(value.getViewModel(), out); 19 | OdooUtils.writeNullableString(value.getInheritId(), out); 20 | } 21 | 22 | @Override 23 | public OdooRecordViewInfo read(@NotNull DataInput in) throws IOException { 24 | String viewType = OdooUtils.readNullableString(in); 25 | String viewModel = OdooUtils.readNullableString(in); 26 | String inheritId = OdooUtils.readNullableString(in); 27 | return new OdooRecordViewInfo(viewType, viewModel, inheritId); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/filter/OdooRecordFilter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data.filter; 2 | 3 | import dev.ngocta.pycharm.odoo.data.OdooRecord; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public interface OdooRecordFilter { 7 | boolean accept(@NotNull OdooRecord record); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/filter/OdooRecordFilters.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data.filter; 2 | 3 | import com.intellij.util.ObjectUtils; 4 | import dev.ngocta.pycharm.odoo.OdooNames; 5 | import dev.ngocta.pycharm.odoo.data.OdooRecordViewInfo; 6 | 7 | public class OdooRecordFilters { 8 | public static final OdooRecordFilter RES_GROUPS = new OdooRecordModelFilter(OdooNames.RES_GROUPS); 9 | public static final OdooRecordFilter IR_UI_MENU = new OdooRecordModelFilter(OdooNames.IR_UI_MENU); 10 | public static final OdooRecordFilter IR_MODEL = new OdooRecordModelFilter(OdooNames.IR_MODEL); 11 | public static final OdooRecordFilter ACTION_MODELS = new OdooRecordModelFilter(OdooNames.ACTION_MODELS); 12 | public static final OdooRecordFilter QWEB = record -> { 13 | OdooRecordViewInfo viewInfo = ObjectUtils.tryCast(record.getExtraInfo(), OdooRecordViewInfo.class); 14 | return viewInfo != null && OdooNames.VIEW_TYPE_QWEB.equals(viewInfo.getViewType()); 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/filter/OdooRecordModelFilter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data.filter; 2 | 3 | import dev.ngocta.pycharm.odoo.data.OdooRecord; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | public class OdooRecordModelFilter implements OdooRecordFilter { 11 | private final List myModels; 12 | 13 | public OdooRecordModelFilter(@Nullable String... models) { 14 | myModels = new LinkedList<>(); 15 | for (String model : models) { 16 | if (model != null) { 17 | myModels.add(model); 18 | } 19 | } 20 | } 21 | 22 | @Override 23 | public boolean accept(@NotNull OdooRecord record) { 24 | return myModels.isEmpty() || myModels.contains(record.getModel()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/data/filter/OdooRecordViewModelFilter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.data.filter; 2 | 3 | import com.intellij.util.ObjectUtils; 4 | import dev.ngocta.pycharm.odoo.data.OdooRecord; 5 | import dev.ngocta.pycharm.odoo.data.OdooRecordViewInfo; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class OdooRecordViewModelFilter implements OdooRecordFilter { 12 | private final String myViewModel; 13 | private final List myExcludedIds; 14 | 15 | public OdooRecordViewModelFilter(@NotNull String viewModel, 16 | String... excludedIds) { 17 | myViewModel = viewModel; 18 | myExcludedIds = Arrays.asList(excludedIds); 19 | } 20 | 21 | @Override 22 | public boolean accept(@NotNull OdooRecord record) { 23 | OdooRecordViewInfo viewInfo = ObjectUtils.tryCast(record.getExtraInfo(), OdooRecordViewInfo.class); 24 | return viewInfo != null && myViewModel.equals(viewInfo.getViewModel()) && !myExcludedIds.contains(record.getQualifiedId()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJQueryCSSLanguageInjector.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.javascript.frameworks.jquery.JQueryCssLanguage; 4 | import com.intellij.lang.javascript.psi.JSArgumentList; 5 | import com.intellij.lang.javascript.psi.JSCallExpression; 6 | import com.intellij.lang.javascript.psi.JSLiteralExpression; 7 | import com.intellij.openapi.util.TextRange; 8 | import com.intellij.psi.*; 9 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class OdooJQueryCSSLanguageInjector implements LanguageInjector { 13 | @Override 14 | public void getLanguagesToInject(@NotNull PsiLanguageInjectionHost host, 15 | @NotNull InjectedLanguagePlaces injectionPlacesRegistrar) { 16 | if (host instanceof JSLiteralExpression && OdooModuleUtils.isInOdooModule(host)) { 17 | PsiElement parent = host.getParent(); 18 | if (parent instanceof JSArgumentList) { 19 | parent = parent.getParent(); 20 | if (parent instanceof JSCallExpression) { 21 | String callExpressionName = OdooJSUtils.getCallFunctionName((JSCallExpression) parent); 22 | if ("$".equals(callExpressionName) && OdooModuleUtils.isInOdooModule(parent)) { 23 | TextRange range = ElementManipulators.getValueTextRange(host); 24 | injectionPlacesRegistrar.addPlace(JQueryCssLanguage.INSTANCE, range, null, null); 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSDialectSpecificHandlersFactory.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.javascript.JavaScriptSpecificHandlersFactory; 4 | import com.intellij.lang.javascript.psi.resolve.JSEvaluateContext; 5 | import com.intellij.lang.javascript.psi.resolve.JSTypeEvaluator; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class OdooJSDialectSpecificHandlersFactory extends JavaScriptSpecificHandlersFactory { 9 | public OdooJSDialectSpecificHandlersFactory() { 10 | } 11 | 12 | @NotNull 13 | @Override 14 | public JSTypeEvaluator newTypeEvaluator(@NotNull JSEvaluateContext context) { 15 | return new OdooJSTypeEvaluator(context); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSElementScopeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.javascript.library.JSLibraryMappings; 4 | import com.intellij.lang.javascript.psi.resolve.JavaScriptResolveScopeProvider; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import com.intellij.psi.search.GlobalSearchScope; 8 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 9 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | public class OdooJSElementScopeProvider extends JavaScriptResolveScopeProvider { 16 | @Override 17 | public GlobalSearchScope getResolveScope(@NotNull VirtualFile file, Project project) { 18 | OdooModule odooModule = OdooModuleUtils.getContainingOdooModule(file, project); 19 | if (odooModule == null) { 20 | return super.getResolveScope(file, project); 21 | } 22 | GlobalSearchScope modulesScope = odooModule.getOdooModuleWithDependenciesScope(); 23 | List predefinedLibraryFiles = this.getPredefinedLibraryFiles(project); 24 | GlobalSearchScope predefinedLibraryScope = GlobalSearchScope.filesScope(project, predefinedLibraryFiles); 25 | GlobalSearchScope libraryScope = JSLibraryMappings.getInstance(project).getLibraryScopeForFileWithoutPredefined( 26 | file, Collections.emptySet(), Collections.emptySet()); 27 | return modulesScope.union(predefinedLibraryScope).union(libraryScope); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSFieldWidgetDescriptionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.psi.ElementDescriptionLocation; 4 | import com.intellij.psi.ElementDescriptionProvider; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.usageView.UsageViewTypeLocation; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public class OdooJSFieldWidgetDescriptionProvider implements ElementDescriptionProvider { 11 | @Override 12 | @Nullable 13 | public String getElementDescription(@NotNull PsiElement element, 14 | @NotNull ElementDescriptionLocation location) { 15 | if (element instanceof OdooJSFieldWidget) { 16 | if (location instanceof UsageViewTypeLocation) { 17 | return "widget"; 18 | } 19 | } 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSFieldWidgetFindUsagesProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.findUsages.FindUsagesProvider; 4 | import com.intellij.psi.PsiElement; 5 | import org.jetbrains.annotations.Nls; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class OdooJSFieldWidgetFindUsagesProvider implements FindUsagesProvider { 10 | @Override 11 | public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { 12 | return psiElement instanceof OdooJSFieldWidget; 13 | } 14 | 15 | @Override 16 | @Nullable 17 | public String getHelpId(@NotNull PsiElement psiElement) { 18 | return null; 19 | } 20 | 21 | @Override 22 | @Nls 23 | @NotNull 24 | public String getType(@NotNull PsiElement element) { 25 | return ""; 26 | } 27 | 28 | @Override 29 | @Nls 30 | @NotNull 31 | public String getDescriptiveName(@NotNull PsiElement element) { 32 | return ""; 33 | } 34 | 35 | @Override 36 | @Nls 37 | @NotNull 38 | public String getNodeText(@NotNull PsiElement element, boolean useFullName) { 39 | return ""; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSFieldWidgetGotoContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.navigation.ChooseByNameContributorEx; 4 | import com.intellij.navigation.NavigationItem; 5 | import com.intellij.psi.search.GlobalSearchScope; 6 | import com.intellij.util.Processor; 7 | import com.intellij.util.indexing.FindSymbolParameters; 8 | import com.intellij.util.indexing.IdFilter; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.Collection; 13 | 14 | public class OdooJSFieldWidgetGotoContributor implements ChooseByNameContributorEx { 15 | @Override 16 | public void processNames(@NotNull Processor processor, 17 | @NotNull GlobalSearchScope scope, 18 | @Nullable IdFilter filter) { 19 | if (scope.getProject() != null) { 20 | Collection widgets = OdooJSFieldWidgetIndex.getAvailableWidgetNames(scope, scope.getProject(), true); 21 | for (String widget : widgets) { 22 | processor.process(widget); 23 | } 24 | } 25 | } 26 | 27 | @Override 28 | public void processElementsWithName(@NotNull String name, 29 | @NotNull Processor processor, 30 | @NotNull FindSymbolParameters parameters) { 31 | Collection elements = OdooJSFieldWidgetIndex.getWidgetsByName( 32 | name, parameters.getSearchScope(), parameters.getProject(), true); 33 | elements.forEach(processor::process); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSFieldWidgetReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 5 | import com.intellij.icons.AllIcons; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiElementResolveResult; 8 | import com.intellij.psi.PsiReferenceBase; 9 | import com.intellij.psi.ResolveResult; 10 | import com.intellij.util.xml.DomElement; 11 | import com.intellij.util.xml.DomUtil; 12 | import com.jetbrains.python.psi.PyUtil; 13 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomViewElement; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | import java.util.Collection; 18 | import java.util.LinkedList; 19 | import java.util.List; 20 | 21 | public class OdooJSFieldWidgetReference extends PsiReferenceBase.Poly { 22 | public OdooJSFieldWidgetReference(@NotNull PsiElement element) { 23 | super(element); 24 | } 25 | 26 | @Nullable 27 | private String getViewType() { 28 | DomElement domElement = DomUtil.getDomElement(getElement()); 29 | if (domElement instanceof OdooDomViewElement) { 30 | return ((OdooDomViewElement) domElement).getViewType(); 31 | } 32 | return null; 33 | } 34 | 35 | @Override 36 | public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) { 37 | return PyUtil.getParameterizedCachedValue(getElement(), getValue(), param -> { 38 | Collection widgets = OdooJSFieldWidgetIndex.getWidgetsByName(getValue(), getElement(), false); 39 | String viewType = getViewType(); 40 | if (viewType != null) { 41 | widgets.removeIf(widget -> widget.getViewType() != null && !widget.getViewType().equals(viewType)); 42 | } 43 | return PsiElementResolveResult.createResults(widgets); 44 | }); 45 | } 46 | 47 | @Override 48 | public Object @NotNull [] getVariants() { 49 | Collection names = OdooJSFieldWidgetIndex.getAvailableWidgetNames(getElement(), false); 50 | List lookupElements = new LinkedList<>(); 51 | for (String name : names) { 52 | lookupElements.add(LookupElementBuilder.create(name).withIcon(AllIcons.Nodes.MultipleTypeDefinitions)); 53 | } 54 | return lookupElements.toArray(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSFieldWidgetReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.PsiReferenceProvider; 6 | import com.intellij.util.ProcessingContext; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class OdooJSFieldWidgetReferenceProvider extends PsiReferenceProvider { 10 | @Override 11 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { 12 | return new PsiReference[]{new OdooJSFieldWidgetReference(element)}; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSModuleDescriptionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.psi.ElementDescriptionLocation; 4 | import com.intellij.psi.ElementDescriptionProvider; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.usageView.UsageViewTypeLocation; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public class OdooJSModuleDescriptionProvider implements ElementDescriptionProvider { 11 | @Override 12 | @Nullable 13 | public String getElementDescription(@NotNull PsiElement element, 14 | @NotNull ElementDescriptionLocation location) { 15 | if (element instanceof OdooJSModule) { 16 | if (location instanceof UsageViewTypeLocation) { 17 | return "module"; 18 | } 19 | } 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSModuleFindUsagesProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.findUsages.FindUsagesProvider; 4 | import com.intellij.psi.PsiElement; 5 | import org.jetbrains.annotations.Nls; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class OdooJSModuleFindUsagesProvider implements FindUsagesProvider { 10 | @Override 11 | public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { 12 | return psiElement instanceof OdooJSModule; 13 | } 14 | 15 | @Override 16 | @Nullable 17 | public String getHelpId(@NotNull PsiElement psiElement) { 18 | return ""; 19 | } 20 | 21 | @Override 22 | @Nls 23 | @NotNull 24 | public String getType(@NotNull PsiElement element) { 25 | return ""; 26 | } 27 | 28 | @Override 29 | @Nls 30 | @NotNull 31 | public String getDescriptiveName(@NotNull PsiElement element) { 32 | return ""; 33 | } 34 | 35 | @Override 36 | @Nls 37 | @NotNull 38 | public String getNodeText(@NotNull PsiElement element, boolean useFullName) { 39 | return ""; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSModuleReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.javascript.JSModuleBaseReference; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiElementResolveResult; 6 | import com.intellij.psi.PsiPolyVariantReferenceBase; 7 | import com.intellij.psi.ResolveResult; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class OdooJSModuleReference extends PsiPolyVariantReferenceBase implements JSModuleBaseReference { 11 | private final String myModuleName; 12 | 13 | public OdooJSModuleReference(@NotNull PsiElement element, 14 | @NotNull String moduleName) { 15 | super(element); 16 | myModuleName = moduleName; 17 | } 18 | 19 | @Override 20 | public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) { 21 | OdooJSModule module = OdooJSModuleIndex.findModule(myModuleName, myElement); 22 | if (module != null) { 23 | return PsiElementResolveResult.createResults(module); 24 | } 25 | return new ResolveResult[0]; 26 | } 27 | 28 | @Override 29 | public Object @NotNull [] getVariants() { 30 | return OdooJSModuleIndex.getAvailableModuleNames(myElement).toArray(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSModuleReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.javascript.frameworks.modules.JSBaseModuleReferenceContributor; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiReference; 6 | import com.intellij.psi.PsiReferenceProvider; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public class OdooJSModuleReferenceContributor extends JSBaseModuleReferenceContributor { 11 | @Override 12 | public boolean isApplicable(@NotNull PsiElement psiElement) { 13 | return OdooJSUtils.isInOdooJSModule(psiElement); 14 | } 15 | 16 | @Override 17 | protected PsiReference @NotNull [] getReferences(@NotNull String text, 18 | @NotNull PsiElement host, 19 | int offset, 20 | @Nullable PsiReferenceProvider psiReferenceProvider, 21 | boolean isCommonJSModule) { 22 | return new PsiReference[]{new OdooJSModuleReference(host, text)}; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSModuleRenameInputValidator.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.patterns.ElementPattern; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.refactoring.rename.RenameInputValidator; 7 | import com.intellij.util.ProcessingContext; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.regex.Pattern; 11 | 12 | public class OdooJSModuleRenameInputValidator implements RenameInputValidator { 13 | @Override 14 | @NotNull 15 | public ElementPattern getPattern() { 16 | return PlatformPatterns.psiElement(OdooJSModule.class); 17 | } 18 | 19 | @Override 20 | public boolean isInputValid(@NotNull String newName, 21 | @NotNull PsiElement element, 22 | @NotNull ProcessingContext context) { 23 | return Pattern.matches("^[a-zA-Z0-9_.]+$", newName); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSTargetElementEvaluator.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.javascript.JSTargetElementEvaluator; 4 | import com.intellij.lang.javascript.psi.*; 5 | import com.intellij.psi.PsiElement; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class OdooJSTargetElementEvaluator extends JSTargetElementEvaluator { 10 | @Override 11 | @Nullable 12 | public PsiElement getNamedElement(@NotNull PsiElement element) { 13 | PsiElement parent = element.getParent(); 14 | if (parent instanceof JSLiteralExpression) { 15 | String stringValue = ((JSLiteralExpression) parent).getStringValue(); 16 | if (stringValue != null) { 17 | parent = parent.getParent(); 18 | if (parent instanceof JSArgumentList) { 19 | parent = parent.getParent(); 20 | if (parent instanceof JSCallExpression) { 21 | JSExpression method = ((JSCallExpression) parent).getMethodExpression(); 22 | if (method instanceof JSReferenceExpression && "odoo.define".equals(method.getText())) { 23 | return new OdooJSModule(stringValue, (JSCallExpression) parent); 24 | } 25 | } 26 | } 27 | } 28 | } 29 | return super.getNamedElement(element); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/javascript/OdooJSUtils.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.javascript; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.javascript.psi.JSCallExpression; 5 | import com.intellij.lang.javascript.psi.JSFile; 6 | import com.intellij.lang.javascript.psi.impl.JSCallExpressionImpl; 7 | import com.intellij.lang.javascript.psi.impl.JSReferenceExpressionImpl; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.psi.PsiElement; 10 | import com.intellij.psi.PsiFile; 11 | import com.intellij.util.indexing.FileBasedIndex; 12 | import dev.ngocta.pycharm.odoo.OdooUtils; 13 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | public class OdooJSUtils { 17 | private OdooJSUtils() { 18 | } 19 | 20 | public static boolean isOdooJSFile(@Nullable PsiFile file) { 21 | return file instanceof JSFile && OdooModuleUtils.isInOdooModule(file); 22 | } 23 | 24 | public static boolean isOdooJSModuleFile(@Nullable PsiFile file) { 25 | if (file == null) { 26 | return false; 27 | } 28 | VirtualFile virtualFile = file.getVirtualFile(); 29 | if (virtualFile == null) { 30 | return false; 31 | } 32 | return !FileBasedIndex.getInstance().getFileData(OdooJSModuleIndex.NAME, virtualFile, file.getProject()).isEmpty(); 33 | } 34 | 35 | public static boolean isInOdooJSModule(@Nullable PsiElement element) { 36 | PsiFile file = OdooUtils.getOriginalContextFile(element); 37 | return isOdooJSModuleFile(file); 38 | } 39 | 40 | @Nullable 41 | public static String getCallFunctionName(@Nullable JSCallExpression callExpression) { 42 | if (callExpression == null) { 43 | return null; 44 | } 45 | ASTNode methodExpression = JSCallExpressionImpl.getMethodExpression(callExpression.getNode()); 46 | if (methodExpression != null) { 47 | return JSReferenceExpressionImpl.getReferenceName(methodExpression); 48 | } 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooCanonicalPathProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.util.QualifiedName; 5 | import com.jetbrains.python.psi.resolve.PyCanonicalPathProvider; 6 | import com.jetbrains.python.psi.resolve.PyResolveImportUtil; 7 | import dev.ngocta.pycharm.odoo.OdooNames; 8 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 9 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.List; 14 | 15 | public class OdooCanonicalPathProvider implements PyCanonicalPathProvider { 16 | @Nullable 17 | @Override 18 | public QualifiedName getCanonicalPath(@NotNull QualifiedName qualifiedName, 19 | @Nullable PsiElement psiElement) { 20 | if (psiElement == null) { 21 | return null; 22 | } 23 | if (qualifiedName.toString().contains(OdooNames.ODOO_ADDONS_QNAME)) { 24 | return null; 25 | } 26 | List elements = PyResolveImportUtil.resolveQualifiedName(qualifiedName, PyResolveImportUtil.fromFoothold(psiElement)); 27 | if (elements.size() != 1) { 28 | return null; 29 | } 30 | PsiElement element = elements.get(0); 31 | OdooModule module = OdooModuleUtils.getContainingOdooModule(element); 32 | if (module == null) { 33 | return null; 34 | } 35 | String moduleName = module.getName(); 36 | List components = qualifiedName.getComponents(); 37 | int moduleNamePos = components.indexOf(moduleName); 38 | if (moduleNamePos < 0) { 39 | return null; 40 | } 41 | QualifiedName relativeNameFromModule = qualifiedName.subQualifiedName(moduleNamePos, components.size()); 42 | return QualifiedName.fromDottedString(OdooNames.ODOO_ADDONS_QNAME).append(relativeNameFromModule); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooEnvironmentItemProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.Ref; 5 | import com.jetbrains.python.PyNames; 6 | import com.jetbrains.python.psi.*; 7 | import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl; 8 | import com.jetbrains.python.psi.types.*; 9 | import dev.ngocta.pycharm.odoo.OdooNames; 10 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClassType; 11 | import dev.ngocta.pycharm.odoo.python.model.OdooRecordSetType; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | public class OdooEnvironmentItemProvider extends PyTypeProviderBase { 16 | @Nullable 17 | @Override 18 | public Ref getCallType(@NotNull PyFunction function, 19 | @NotNull PyCallSiteExpression callSite, 20 | @NotNull TypeEvalContext context) { 21 | String functionName = function.getName(); 22 | if (PyNames.GETITEM.equals(functionName) && callSite instanceof PySubscriptionExpression) { 23 | PySubscriptionExpression subscription = (PySubscriptionExpression) callSite; 24 | Project project = subscription.getProject(); 25 | PyExpression operand = subscription.getOperand(); 26 | if (OdooPyUtils.isEnvironmentTypeExpression(operand, context)) { 27 | PyExpression index = subscription.getIndexExpression(); 28 | if (index instanceof PyLiteralExpression) { 29 | String model = ((PyStringLiteralExpressionImpl) index).getStringValue(); 30 | OdooModelClassType modelClassType = new OdooModelClassType(model, OdooRecordSetType.MODEL, project); 31 | return Ref.create(modelClassType); 32 | } else { 33 | PyClassType type = OdooPyUtils.getClassTypeByQName(OdooNames.BASE_MODEL_CLASS_QNAME, function, false); 34 | return Ref.create(PyUnionType.createWeakType(type)); 35 | } 36 | } 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooFunctionParamBooleanTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.jetbrains.python.psi.PyBoolLiteralExpression; 5 | import com.jetbrains.python.psi.PyFunction; 6 | import com.jetbrains.python.psi.PyNamedParameter; 7 | import com.jetbrains.python.psi.types.PyType; 8 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 9 | import com.jetbrains.python.psi.types.TypeEvalContext; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class OdooFunctionParamBooleanTypeProvider extends PyTypeProviderBase { 14 | @Nullable 15 | @Override 16 | public Ref getParameterType(@NotNull PyNamedParameter param, 17 | @NotNull PyFunction func, 18 | @NotNull TypeEvalContext context) { 19 | if (param.getDefaultValue() instanceof PyBoolLiteralExpression) { 20 | // In Odoo, the default value for a recordset param is usually False, 21 | // that causes PyCharm infers its type is bool. 22 | // To avoid errors such as "Unresolved attribute reference xyz for class bool", 23 | // its type should be Any instead. 24 | return Ref.create(); 25 | } 26 | return super.getParameterType(param, func, context); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooImportCandidateProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiNamedElement; 5 | import com.intellij.psi.PsiReference; 6 | import com.intellij.psi.util.QualifiedName; 7 | import com.jetbrains.python.codeInsight.imports.AutoImportQuickFix; 8 | import com.jetbrains.python.codeInsight.imports.PyImportCandidateProvider; 9 | import com.jetbrains.python.psi.resolve.PyResolveImportUtil; 10 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 11 | 12 | public class OdooImportCandidateProvider implements PyImportCandidateProvider { 13 | @Override 14 | public void addImportCandidates(PsiReference psiReference, 15 | String s, 16 | AutoImportQuickFix autoImportQuickFix) { 17 | PsiElement element = psiReference.getElement(); 18 | if ("_".equals(s) && OdooModuleUtils.isInOdooModule(element)) { 19 | QualifiedName qualifiedName = QualifiedName.fromDottedString("odoo.tools.translate._"); 20 | PsiElement importElement = PyResolveImportUtil.resolveTopLevelMember(qualifiedName, PyResolveImportUtil.fromFoothold(element)); 21 | if (importElement instanceof PsiNamedElement) { 22 | autoImportQuickFix.addImport((PsiNamedElement) importElement, importElement.getContainingFile(), QualifiedName.fromComponents("odoo"), null); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooInitHookFunctionParamTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.intellij.psi.PsiDirectory; 5 | import com.intellij.psi.PsiFile; 6 | import com.jetbrains.python.PyNames; 7 | import com.jetbrains.python.psi.PyFunction; 8 | import com.jetbrains.python.psi.PyNamedParameter; 9 | import com.jetbrains.python.psi.PyParameter; 10 | import com.jetbrains.python.psi.types.PyType; 11 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 12 | import com.jetbrains.python.psi.types.TypeEvalContext; 13 | import dev.ngocta.pycharm.odoo.OdooNames; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | public class OdooInitHookFunctionParamTypeProvider extends PyTypeProviderBase { 18 | @Nullable 19 | @Override 20 | public Ref getParameterType(@NotNull PyNamedParameter param, 21 | @NotNull PyFunction func, 22 | @NotNull TypeEvalContext context) { 23 | String funcName = func.getName(); 24 | if (funcName == null) { 25 | return null; 26 | } 27 | PsiFile file = context.getOrigin(); 28 | if (file == null) { 29 | return null; 30 | } 31 | if (!PyNames.INIT_DOT_PY.equals(file.getName())) { 32 | return null; 33 | } 34 | PsiDirectory dir = file.getContainingDirectory(); 35 | if (dir == null) { 36 | return null; 37 | } 38 | if (dir.findFile(OdooNames.MANIFEST_FILE_NAME) == null) { 39 | return null; 40 | } 41 | PyParameter[] parameters = func.getParameterList().getParameters(); 42 | if (parameters.length > 0 && parameters[0] == param && "cr".equals(param.getName())) { 43 | return Ref.create(OdooPyUtils.getDbCursorType(dir)); 44 | } else if (parameters.length > 1 && parameters[1] == param && param.getName() != null && param.getName().startsWith("reg")) { 45 | return Ref.create(OdooPyUtils.getRegistryType(dir)); 46 | } 47 | return null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooMigrateFunctionParamTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.intellij.psi.PsiDirectory; 5 | import com.intellij.psi.PsiFile; 6 | import com.jetbrains.python.psi.PyFunction; 7 | import com.jetbrains.python.psi.PyNamedParameter; 8 | import com.jetbrains.python.psi.PyParameter; 9 | import com.jetbrains.python.psi.impl.PyBuiltinCache; 10 | import com.jetbrains.python.psi.types.PyType; 11 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 12 | import com.jetbrains.python.psi.types.TypeEvalContext; 13 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 14 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | public class OdooMigrateFunctionParamTypeProvider extends PyTypeProviderBase { 19 | @Nullable 20 | @Override 21 | public Ref getParameterType(@NotNull PyNamedParameter param, 22 | @NotNull PyFunction func, 23 | @NotNull TypeEvalContext context) { 24 | String funcName = func.getName(); 25 | if (!"migrate".equals(funcName)) { 26 | return null; 27 | } 28 | PsiFile file = context.getOrigin(); 29 | if (file == null) { 30 | return null; 31 | } 32 | PsiDirectory dir = file.getParent(); 33 | if (dir == null) { 34 | return null; 35 | } 36 | dir = dir.getParent(); 37 | if (dir == null) { 38 | return null; 39 | } 40 | if (!"migrations".equals(dir.getName())) { 41 | return null; 42 | } 43 | OdooModule module = OdooModuleUtils.getContainingOdooModule(dir); 44 | if (module == null) { 45 | return null; 46 | } 47 | PyParameter[] parameters = func.getParameterList().getParameters(); 48 | if (parameters.length > 0 && parameters[0] == param) { 49 | return Ref.create(OdooPyUtils.getDbCursorType(dir)); 50 | } else if (parameters.length > 1 && parameters[1] == param) { 51 | return Ref.create(PyBuiltinCache.getInstance(func).getStrType()); 52 | } 53 | return null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooPyDocStringTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.util.indexing.FileBasedIndex; 6 | import com.jetbrains.python.psi.PyFunction; 7 | import com.jetbrains.python.psi.PyNamedParameter; 8 | import com.jetbrains.python.psi.StructuredDocString; 9 | import com.jetbrains.python.psi.types.PyType; 10 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 11 | import com.jetbrains.python.psi.types.TypeEvalContext; 12 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClassType; 13 | import dev.ngocta.pycharm.odoo.python.model.OdooModelIndex; 14 | import dev.ngocta.pycharm.odoo.python.model.OdooRecordSetType; 15 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 16 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 17 | import org.jetbrains.annotations.NotNull; 18 | import org.jetbrains.annotations.Nullable; 19 | 20 | public class OdooPyDocStringTypeProvider extends PyTypeProviderBase { 21 | @Nullable 22 | @Override 23 | public Ref getParameterType(@NotNull PyNamedParameter param, 24 | @NotNull PyFunction func, 25 | @NotNull TypeEvalContext context) { 26 | StructuredDocString docString = func.getStructuredDocString(); 27 | if (docString != null) { 28 | String typeText = docString.getParamType(param.getName()); 29 | if (typeText != null) { 30 | if (isOdooModelName(typeText, func)) { 31 | return new Ref<>(new OdooModelClassType(typeText, OdooRecordSetType.MULTI, func.getProject())); 32 | } 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | private static boolean isOdooModelName(@NotNull String name, 39 | @NotNull PsiElement anchor) { 40 | OdooModule module = OdooModuleUtils.getContainingOdooModule(anchor); 41 | if (module == null) { 42 | return false; 43 | } 44 | Ref is = new Ref<>(false); 45 | FileBasedIndex.getInstance().processValues(OdooModelIndex.NAME, name, null, (file, value) -> { 46 | is.set(true); 47 | return false; 48 | }, module.getOdooModuleWithDependenciesScope()); 49 | return is.get(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooPyFunctionSelfParamNameMacro.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.codeInsight.template.*; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.util.PsiTreeUtil; 6 | import com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType; 7 | import com.jetbrains.python.psi.PyFunction; 8 | import com.jetbrains.python.psi.PyParameter; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class OdooPyFunctionSelfParamNameMacro extends Macro { 13 | @Override 14 | public String getName() { 15 | return "pyFunctionSelfParamName"; 16 | } 17 | 18 | @Override 19 | public String getPresentableName() { 20 | return "pyFunctionSelfParamName()"; 21 | } 22 | 23 | @Override 24 | @Nullable 25 | public Result calculateResult(Expression @NotNull [] params, ExpressionContext context) { 26 | String selfName = "self"; 27 | PsiElement place = context.getPsiElementAtStartOffset(); 28 | PyFunction pyFunction = PsiTreeUtil.getParentOfType(place, PyFunction.class); 29 | if (pyFunction != null) { 30 | PyParameter[] pyParameters = pyFunction.getParameterList().getParameters(); 31 | if (pyParameters.length > 0 && pyParameters[0].isSelf()) { 32 | String name = pyParameters[0].getName(); 33 | if (name != null) { 34 | selfName = name; 35 | } 36 | } 37 | } 38 | return new TextResult(selfName); 39 | } 40 | 41 | @Override 42 | public boolean isAcceptableInContext(TemplateContextType context) { 43 | return context instanceof PythonTemplateContextType; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooPyInspectionExtension.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.xml.XmlFile; 6 | import com.jetbrains.python.inspections.PyInspectionExtension; 7 | import com.jetbrains.python.psi.PyElement; 8 | import com.jetbrains.python.psi.PyExpression; 9 | import com.jetbrains.python.psi.PyExpressionStatement; 10 | import com.jetbrains.python.psi.PyReferenceExpression; 11 | import com.jetbrains.python.psi.types.TypeEvalContext; 12 | import dev.ngocta.pycharm.odoo.OdooNames; 13 | import dev.ngocta.pycharm.odoo.python.model.OdooModelUtils; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | public class OdooPyInspectionExtension extends PyInspectionExtension { 17 | public boolean ignoreUnresolvedReference(@NotNull PyElement node, 18 | @NotNull PsiReference reference, 19 | @NotNull TypeEvalContext context) { 20 | if (context.getOrigin() instanceof XmlFile && node instanceof PyReferenceExpression) { 21 | PyReferenceExpression referenceExpression = (PyReferenceExpression) node; 22 | PyExpression qualifier = referenceExpression.getQualifier(); 23 | if (qualifier == null) { 24 | return true; 25 | } else if (qualifier instanceof PyReferenceExpression) { 26 | referenceExpression = (PyReferenceExpression) qualifier; 27 | return referenceExpression.getQualifier() == null && "object".equals(referenceExpression.getName()); 28 | } 29 | } 30 | return OdooModelUtils.getSearchDomainContextResolver(node, false) != null; 31 | } 32 | 33 | @Override 34 | public boolean ignoreNoEffectStatement(@NotNull PyExpressionStatement expressionStatement) { 35 | PsiFile file = expressionStatement.getContainingFile(); 36 | if (file == null) { 37 | return true; 38 | } 39 | if (file.getContext() != null) { 40 | return true; 41 | } 42 | if (OdooNames.MANIFEST_FILE_NAME.equals(file.getName())) { 43 | return true; 44 | } 45 | return super.ignoreNoEffectStatement(expressionStatement); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooPyRunnableScriptFilter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.execution.Location; 4 | import com.intellij.openapi.module.Module; 5 | import com.intellij.psi.PsiFile; 6 | import com.jetbrains.python.psi.types.TypeEvalContext; 7 | import com.jetbrains.python.run.RunnableScriptFilter; 8 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class OdooPyRunnableScriptFilter implements RunnableScriptFilter { 13 | @Override 14 | public boolean isRunnableScript(PsiFile psiFile, 15 | @NotNull Module module, 16 | Location location, 17 | @Nullable TypeEvalContext typeEvalContext) { 18 | return OdooModuleUtils.isInOdooModule(psiFile); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooPySuperFunctionParamMacro.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.codeInsight.template.*; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.util.PsiTreeUtil; 6 | import com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType; 7 | import com.jetbrains.python.psi.PyFunction; 8 | import com.jetbrains.python.psi.PyNamedParameter; 9 | import com.jetbrains.python.psi.PyParameter; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.LinkedList; 14 | import java.util.List; 15 | 16 | public class OdooPySuperFunctionParamMacro extends Macro { 17 | @Override 18 | public String getName() { 19 | return "pySuperFunctionParam"; 20 | } 21 | 22 | @Override 23 | public String getPresentableName() { 24 | return "pySuperFunctionParam()"; 25 | } 26 | 27 | @Override 28 | @Nullable 29 | public Result calculateResult(Expression @NotNull [] params, ExpressionContext context) { 30 | PsiElement place = context.getPsiElementAtStartOffset(); 31 | PyFunction pyFunction = PsiTreeUtil.getParentOfType(place, PyFunction.class); 32 | if (pyFunction == null) { 33 | return null; 34 | } 35 | PyParameter[] pyParameters = pyFunction.getParameterList().getParameters(); 36 | List paramNames = new LinkedList<>(); 37 | for (PyParameter pyParameter : pyParameters) { 38 | PyNamedParameter namedParameter = pyParameter.getAsNamed(); 39 | if (namedParameter != null && !namedParameter.isSelf()) { 40 | paramNames.add(namedParameter.getRepr(false)); 41 | } 42 | } 43 | String text = String.join(", ", paramNames); 44 | return new TextResult(text); 45 | } 46 | 47 | @Override 48 | public boolean isAcceptableInContext(TemplateContextType context) { 49 | return context instanceof PythonTemplateContextType; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooPythonParserDefinition.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.python.PythonParserDefinition; 6 | import com.jetbrains.python.psi.impl.*; 7 | import dev.ngocta.pycharm.odoo.python.psi.*; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class OdooPythonParserDefinition extends PythonParserDefinition { 11 | @NotNull 12 | @Override 13 | public PsiElement createElement(@NotNull ASTNode node) { 14 | PsiElement element = super.createElement(node); 15 | if (element instanceof PyReferenceExpressionImpl) { 16 | element = new OdooPyReferenceExpression(node); 17 | } else if (element instanceof PySubscriptionExpressionImpl) { 18 | element = new OdooPySubscriptionExpression(node); 19 | } else if (element instanceof PySliceExpressionImpl) { 20 | element = new OdooPySliceExpression(node); 21 | } else if (element instanceof PyCallExpressionImpl) { 22 | element = new OdooPyCallExpression(node); 23 | } else if (element instanceof PyStringLiteralExpressionImpl) { 24 | element = new OdooPyStringLiteralExpression(node); 25 | } else if (element instanceof PyTargetExpressionImpl) { 26 | element = new OdooPyTargetExpression(node); 27 | } else if (element instanceof PyNamedParameterImpl) { 28 | element = new OdooPyNamedParameter(node); 29 | } 30 | return element; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooRefCallTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.Ref; 5 | import com.intellij.openapi.util.text.StringUtil; 6 | import com.jetbrains.python.psi.PyCallExpression; 7 | import com.jetbrains.python.psi.PyCallSiteExpression; 8 | import com.jetbrains.python.psi.PyFunction; 9 | import com.jetbrains.python.psi.PyStringLiteralExpression; 10 | import com.jetbrains.python.psi.types.PyType; 11 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 12 | import com.jetbrains.python.psi.types.TypeEvalContext; 13 | import dev.ngocta.pycharm.odoo.OdooNames; 14 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdIndex; 15 | import dev.ngocta.pycharm.odoo.data.OdooRecord; 16 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClassType; 17 | import dev.ngocta.pycharm.odoo.python.model.OdooRecordSetType; 18 | import org.jetbrains.annotations.NotNull; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.util.Collection; 22 | 23 | public class OdooRefCallTypeProvider extends PyTypeProviderBase { 24 | @Nullable 25 | @Override 26 | public Ref getCallType(@NotNull PyFunction function, 27 | @NotNull PyCallSiteExpression callSite, 28 | @NotNull TypeEvalContext context) { 29 | Project project = function.getProject(); 30 | if (OdooNames.ENV_REF_FUNC_QNAME.equals(function.getQualifiedName()) && callSite instanceof PyCallExpression) { 31 | PyStringLiteralExpression idExpression = ((PyCallExpression) callSite).getArgument(0, PyStringLiteralExpression.class); 32 | if (idExpression != null) { 33 | String id = idExpression.getStringValue(); 34 | Collection records = OdooExternalIdIndex.findRecordsByQualifiedId(id, callSite); 35 | if (!records.isEmpty()) { 36 | String model = records.iterator().next().getModel(); 37 | if (StringUtil.isNotEmpty(model)) { 38 | return new Ref<>(new OdooModelClassType(model, OdooRecordSetType.ONE, project)); 39 | } 40 | } 41 | } 42 | } 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooRegistryItemProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.Ref; 5 | import com.jetbrains.python.PyNames; 6 | import com.jetbrains.python.psi.*; 7 | import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl; 8 | import com.jetbrains.python.psi.types.*; 9 | import dev.ngocta.pycharm.odoo.OdooNames; 10 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClassType; 11 | import dev.ngocta.pycharm.odoo.python.model.OdooRecordSetType; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | public class OdooRegistryItemProvider extends PyTypeProviderBase { 16 | @Nullable 17 | @Override 18 | public Ref getCallType(@NotNull PyFunction function, 19 | @NotNull PyCallSiteExpression callSite, 20 | @NotNull TypeEvalContext context) { 21 | String functionName = function.getName(); 22 | if (PyNames.GETITEM.equals(functionName) && callSite instanceof PySubscriptionExpression) { 23 | PySubscriptionExpression subscription = (PySubscriptionExpression) callSite; 24 | Project project = subscription.getProject(); 25 | PyExpression operand = subscription.getOperand(); 26 | if (OdooPyUtils.isRegistryTypeExpression(operand, context)) { 27 | PyExpression index = subscription.getIndexExpression(); 28 | if (index instanceof PyLiteralExpression) { 29 | String model = ((PyStringLiteralExpressionImpl) index).getStringValue(); 30 | OdooModelClassType modelClassType = new OdooModelClassType(model, OdooRecordSetType.NONE, project); 31 | return Ref.create(modelClassType); 32 | } else { 33 | PyClassType type = OdooPyUtils.getClassTypeByQName(OdooNames.BASE_MODEL_CLASS_QNAME, function, true); 34 | return Ref.create(PyUnionType.createWeakType(type)); 35 | } 36 | } 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooSelfTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.jetbrains.python.psi.PyClass; 5 | import com.jetbrains.python.psi.PyFunction; 6 | import com.jetbrains.python.psi.PyNamedParameter; 7 | import com.jetbrains.python.psi.types.PyClassTypeImpl; 8 | import com.jetbrains.python.psi.types.PyType; 9 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 10 | import com.jetbrains.python.psi.types.TypeEvalContext; 11 | import dev.ngocta.pycharm.odoo.python.model.OdooModelUtils; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | public class OdooSelfTypeProvider extends PyTypeProviderBase { 16 | @Override 17 | @Nullable 18 | public Ref getParameterType(@NotNull PyNamedParameter param, 19 | @NotNull PyFunction func, 20 | @NotNull TypeEvalContext context) { 21 | if (param.isSelf()) { 22 | final PyClass containingClass = func.getContainingClass(); 23 | if (containingClass != null && !OdooModelUtils.isInOdooModelClass(containingClass)) { 24 | final PyFunction.Modifier modifier = func.getModifier(); 25 | return Ref.create(new PyClassTypeImpl(containingClass, modifier == PyFunction.Modifier.CLASSMETHOD)); 26 | } 27 | } 28 | return super.getParameterType(param, func, context); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooTemplateVariableTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.intellij.psi.xml.XmlElement; 5 | import com.intellij.util.ArrayUtil; 6 | import com.intellij.util.xml.DomElement; 7 | import com.intellij.util.xml.DomUtil; 8 | import com.jetbrains.python.psi.PyReferenceExpression; 9 | import com.jetbrains.python.psi.types.PyType; 10 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 11 | import com.jetbrains.python.psi.types.TypeEvalContext; 12 | import dev.ngocta.pycharm.odoo.OdooNames; 13 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomViewElement; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | public class OdooTemplateVariableTypeProvider extends PyTypeProviderBase { 18 | @Override 19 | @Nullable 20 | public PyType getReferenceExpressionType(@NotNull PyReferenceExpression referenceExpression, 21 | @NotNull TypeEvalContext context) { 22 | String name = referenceExpression.getName(); 23 | if (!referenceExpression.isQualified() 24 | && ArrayUtil.contains(name, "env", "request")) { 25 | PsiFile file = referenceExpression.getContainingFile(); 26 | if (file != null && file.getContext() instanceof XmlElement) { 27 | DomElement domElement = DomUtil.getDomElement(file.getContext()); 28 | if (domElement instanceof OdooDomViewElement 29 | && OdooNames.VIEW_TYPE_QWEB.equals(((OdooDomViewElement) domElement).getViewType())) { 30 | if ("env".equals(name)) { 31 | return OdooPyUtils.getEnvironmentType(file); 32 | } else if ("request".equals(name)) { 33 | return OdooPyUtils.getHttpRequestType(file); 34 | } 35 | } 36 | } 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooTestClassMembersProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.jetbrains.python.codeInsight.PyCustomMember; 5 | import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; 6 | import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; 7 | import com.jetbrains.python.psi.PyClass; 8 | import com.jetbrains.python.psi.PyFunction; 9 | import com.jetbrains.python.psi.PyTargetExpression; 10 | import com.jetbrains.python.psi.impl.PyClassImpl; 11 | import com.jetbrains.python.psi.types.PyClassMembersProviderBase; 12 | import com.jetbrains.python.psi.types.PyClassType; 13 | import com.jetbrains.python.psi.types.TypeEvalContext; 14 | import dev.ngocta.pycharm.odoo.OdooNames; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.*; 18 | 19 | public class OdooTestClassMembersProvider extends PyClassMembersProviderBase { 20 | @Override 21 | @NotNull 22 | public Collection getMembers(PyClassType classType, PsiElement location, @NotNull TypeEvalContext context) { 23 | PyClass cls = classType.getPyClass(); 24 | if (location == null) { 25 | return Collections.emptyList(); 26 | } 27 | if (classType.isDefinition() && cls.isSubclass(OdooNames.TEST_BASE_CASE_QNAME, context)) { 28 | List members = new LinkedList<>(); 29 | ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(location); 30 | List setUpClassMethods = cls.multiFindMethodByName("setUpClass", true, context); 31 | if (scopeOwner instanceof PyFunction) { 32 | setUpClassMethods.remove(scopeOwner); 33 | } 34 | Map attributesInSetUpClassMethods = new HashMap<>(); 35 | for (PyFunction method : setUpClassMethods) { 36 | PyClassImpl.collectInstanceAttributes(method, attributesInSetUpClassMethods); 37 | } 38 | for (PyTargetExpression attr : attributesInSetUpClassMethods.values()) { 39 | if (attr.getName() != null) { 40 | members.add(new PyCustomMember(attr.getName(), attr)); 41 | } 42 | } 43 | return members; 44 | } 45 | return Collections.emptyList(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/OdooTranslationStringTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.jetbrains.python.psi.PyCallSiteExpression; 5 | import com.jetbrains.python.psi.PyFunction; 6 | import com.jetbrains.python.psi.impl.PyBuiltinCache; 7 | import com.jetbrains.python.psi.types.PyType; 8 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 9 | import com.jetbrains.python.psi.types.TypeEvalContext; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class OdooTranslationStringTypeProvider extends PyTypeProviderBase { 14 | @Override 15 | @Nullable 16 | public Ref getCallType(@NotNull PyFunction function, 17 | @NotNull PyCallSiteExpression callSite, 18 | @NotNull TypeEvalContext context) { 19 | if (OdooPyUtils.isTranslationStringExpression(callSite)) { 20 | PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(callSite); 21 | return Ref.create(builtinCache.getStrType()); 22 | } 23 | return super.getCallType(function, callSite, context); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooFieldPathReferences.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.openapi.util.Computable; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.ElementManipulators; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiReference; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | 14 | public class OdooFieldPathReferences { 15 | private final PsiElement myElement; 16 | private final OdooModelClass myModelClass; 17 | private final Computable myModelClassResolver; 18 | private PsiReference[] myReferences; 19 | private String[] myFieldNames; 20 | 21 | private OdooFieldPathReferences(@NotNull PsiElement element, 22 | @Nullable OdooModelClass modelClass, 23 | @Nullable Computable modelClassResolver) { 24 | myElement = element; 25 | myModelClass = modelClass; 26 | myModelClassResolver = modelClassResolver; 27 | } 28 | 29 | public static OdooFieldPathReferences create(@NotNull PsiElement element, 30 | @Nullable OdooModelClass modelClass, 31 | @Nullable Computable modelClassResolver) { 32 | OdooFieldPathReferences fieldPathReferences = new OdooFieldPathReferences(element, modelClass, modelClassResolver); 33 | List references = new LinkedList<>(); 34 | TextRange range = ElementManipulators.getValueTextRange(element); 35 | String rangeValue = range.substring(element.getText()); 36 | String[] fieldNames = rangeValue.split("\\."); 37 | int idx = range.getStartOffset(); 38 | for (String name : fieldNames) { 39 | TextRange subRange = TextRange.from(idx, name.length()); 40 | references.add(new OdooFieldReference(element, subRange, fieldPathReferences)); 41 | idx = subRange.getEndOffset() + 1; 42 | } 43 | fieldPathReferences.myFieldNames = fieldNames; 44 | fieldPathReferences.myReferences = references.toArray(PsiReference.EMPTY_ARRAY); 45 | return fieldPathReferences; 46 | } 47 | 48 | public String[] getFieldNames() { 49 | return myFieldNames; 50 | } 51 | 52 | public PsiReference[] getReferences() { 53 | return myReferences; 54 | } 55 | 56 | public OdooModelClass getModelClass() { 57 | return myModelClass; 58 | } 59 | 60 | public Computable getModelClassResolver() { 61 | return myModelClassResolver; 62 | } 63 | 64 | public PsiElement getElement() { 65 | return myElement; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooFieldTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.python.psi.PyCallExpression; 6 | import com.jetbrains.python.psi.PyExpression; 7 | import com.jetbrains.python.psi.PyReferenceExpression; 8 | import com.jetbrains.python.psi.PyTargetExpression; 9 | import com.jetbrains.python.psi.types.PyType; 10 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 11 | import com.jetbrains.python.psi.types.PyUnionType; 12 | import com.jetbrains.python.psi.types.TypeEvalContext; 13 | import dev.ngocta.pycharm.odoo.python.OdooPyUtils; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | public class OdooFieldTypeProvider extends PyTypeProviderBase { 18 | @Nullable 19 | @Override 20 | public PyType getReferenceExpressionType(@NotNull PyReferenceExpression referenceExpression, 21 | @NotNull TypeEvalContext context) { 22 | String referenceName = referenceExpression.getName(); 23 | if (referenceName == null) { 24 | return null; 25 | } 26 | if (referenceExpression.getParent() instanceof PyCallExpression) { 27 | return null; 28 | } 29 | PyExpression qualifier = referenceExpression.getQualifier(); 30 | if (qualifier == null) { 31 | return null; 32 | } 33 | PyType qualifierType = context.getType(qualifier); 34 | OdooModelClassType modelType = OdooModelUtils.extractOdooModelClassType(qualifierType); 35 | if (modelType != null && modelType.getRecordSetType() != OdooRecordSetType.NONE) { 36 | Ref ref = new Ref<>(); 37 | PsiElement element = modelType.resolvePsiMember(referenceName, context); 38 | if (element instanceof PyTargetExpression) { 39 | PyType type = OdooFieldInfo.getFieldType(element, context); 40 | ref.set(type); 41 | } 42 | return ref.get(); 43 | } 44 | if (OdooPyUtils.isUnknownType(qualifierType) && (referenceName.endsWith("_id") || referenceName.endsWith("_ids"))) { 45 | return PyUnionType.createWeakType(OdooModelUtils.guessFieldTypeByName(referenceName, referenceExpression, context)); 46 | } 47 | return null; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelClassSuperType.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.util.Processor; 5 | import com.jetbrains.python.psi.PyClass; 6 | import com.jetbrains.python.psi.types.TypeEvalContext; 7 | import gnu.trove.THashSet; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | public class OdooModelClassSuperType extends OdooModelClassType { 14 | private final OdooModelClassType myOrigin; 15 | private final PyClass myAnchor; 16 | 17 | public OdooModelClassSuperType(@NotNull OdooModelClassType modelClassType, 18 | @NotNull PyClass anchor) { 19 | super(modelClassType.getPyClass(), modelClassType.getRecordSetType()); 20 | myOrigin = modelClassType; 21 | myAnchor = anchor; 22 | } 23 | 24 | @NotNull 25 | @Override 26 | public String getName() { 27 | return "super(" + super.getName() + ")"; 28 | } 29 | 30 | @Override 31 | public void visitMembers(@NotNull Processor processor, 32 | boolean inherited, 33 | @NotNull TypeEvalContext context) { 34 | if (inherited) { 35 | List ancestors = getPyClass().getAncestorClasses(context); 36 | int anchorIndex = ancestors.indexOf(myAnchor); 37 | if (anchorIndex >= 0) { 38 | ancestors = ancestors.subList(0, anchorIndex + 1); 39 | } 40 | Set ignoredMembers = new THashSet<>(); 41 | for (PyClass ancestor : ancestors) { 42 | ancestor.processClassLevelDeclarations((element, state) -> { 43 | ignoredMembers.add(element); 44 | return true; 45 | }); 46 | } 47 | super.visitMembers(element -> { 48 | if (!ignoredMembers.contains(element)) { 49 | return processor.process(element); 50 | } 51 | return true; 52 | }, true, context); 53 | } 54 | } 55 | 56 | public OdooModelClassType getOrigin() { 57 | return myOrigin; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelFunctionPublicReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.jetbrains.python.psi.PyFunction; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.Collection; 8 | 9 | public class OdooModelFunctionPublicReference extends OdooModelFunctionReference { 10 | public OdooModelFunctionPublicReference(@NotNull PsiElement element, 11 | @NotNull String model) { 12 | super(element, model); 13 | } 14 | 15 | @Override 16 | protected Collection getFunctions() { 17 | Collection functions = super.getFunctions(); 18 | functions.removeIf(function -> { 19 | String name = function.getName(); 20 | return name != null && name.startsWith("_"); 21 | }); 22 | return functions; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelFunctionReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.PsiReferenceBase; 6 | import com.jetbrains.python.psi.PyFunction; 7 | import com.jetbrains.python.psi.PyUtil; 8 | import com.jetbrains.python.psi.types.TypeEvalContext; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.*; 13 | 14 | public class OdooModelFunctionReference extends PsiReferenceBase implements PsiReference { 15 | private final OdooModelClass myModelClass; 16 | 17 | public OdooModelFunctionReference(@NotNull PsiElement element, 18 | @NotNull String model) { 19 | this(element, OdooModelClass.getInstance(model, element.getProject())); 20 | } 21 | 22 | public OdooModelFunctionReference(@NotNull PsiElement element, 23 | @NotNull OdooModelClass cls) { 24 | super(element); 25 | myModelClass = cls; 26 | } 27 | 28 | @Nullable 29 | @Override 30 | public PsiElement resolve() { 31 | return PyUtil.getNullableParameterizedCachedValue(getElement(), null, param -> { 32 | TypeEvalContext context = TypeEvalContext.codeAnalysis(getElement().getProject(), getElement().getContainingFile()); 33 | return myModelClass.findMethodByName(getValue(), true, context); 34 | }); 35 | } 36 | 37 | @Override 38 | public Object @NotNull [] getVariants() { 39 | return getFunctions().toArray(); 40 | } 41 | 42 | protected Collection getFunctions() { 43 | TypeEvalContext context = TypeEvalContext.codeCompletion(getElement().getProject(), getElement().getContainingFile()); 44 | List functions = new LinkedList<>(); 45 | Set visitedNames = new HashSet<>(); 46 | myModelClass.visitMethods(function -> { 47 | String name = function.getName(); 48 | if (name != null && !visitedNames.contains(name)) { 49 | functions.add(function); 50 | visitedNames.add(name); 51 | } 52 | return true; 53 | }, true, context); 54 | return functions; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelFunctionReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.patterns.PatternCondition; 4 | import com.intellij.patterns.PsiElementPattern; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiReferenceContributor; 7 | import com.intellij.psi.PsiReferenceRegistrar; 8 | import com.intellij.util.ArrayUtil; 9 | import com.intellij.util.ProcessingContext; 10 | import com.jetbrains.python.psi.PyArgumentList; 11 | import com.jetbrains.python.psi.PyCallExpression; 12 | import com.jetbrains.python.psi.PyKeywordArgument; 13 | import com.jetbrains.python.psi.PyStringLiteralExpression; 14 | import dev.ngocta.pycharm.odoo.OdooNames; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import static com.intellij.patterns.PlatformPatterns.psiElement; 18 | 19 | public class OdooModelFunctionReferenceContributor extends PsiReferenceContributor { 20 | public static final PsiElementPattern.Capture FIELD_ATTR_PATTERN = 21 | psiElement(PyStringLiteralExpression.class).with(new PatternCondition("") { 22 | @Override 23 | public boolean accepts(@NotNull PyStringLiteralExpression pyStringLiteralExpression, ProcessingContext context) { 24 | PsiElement parent = pyStringLiteralExpression.getParent(); 25 | if (parent instanceof PyKeywordArgument) { 26 | if (ArrayUtil.contains(((PyKeywordArgument) parent).getKeyword(), 27 | OdooNames.FIELD_ATTR_COMPUTE, 28 | OdooNames.FIELD_ATTR_INVERSE, 29 | OdooNames.FIELD_ATTR_SEARCH, 30 | OdooNames.FIELD_ATTR_GROUP_EXPAND)) { 31 | parent = parent.getParent(); 32 | if (parent instanceof PyArgumentList) { 33 | parent = parent.getParent(); 34 | return parent instanceof PyCallExpression 35 | && OdooModelUtils.isFieldDeclarationExpression((PyCallExpression) parent); 36 | } 37 | } 38 | } 39 | return false; 40 | } 41 | }); 42 | 43 | @Override 44 | public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { 45 | registrar.registerReferenceProvider(FIELD_ATTR_PATTERN, new OdooModelFunctionReferenceProvider()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelFunctionReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.PsiReferenceProvider; 6 | import com.intellij.util.ProcessingContext; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class OdooModelFunctionReferenceProvider extends PsiReferenceProvider { 10 | @Override 11 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 12 | @NotNull ProcessingContext context) { 13 | OdooModelClass cls = OdooModelUtils.getContainingOdooModelClass(element); 14 | if (cls != null) { 15 | return new PsiReference[]{new OdooModelFunctionReference(element, cls)}; 16 | } else { 17 | return new PsiReference[0]; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelInputFilter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.openapi.fileTypes.FileType; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.util.Consumer; 6 | import com.intellij.util.indexing.FileBasedIndex; 7 | import com.jetbrains.python.PythonFileType; 8 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class OdooModelInputFilter implements FileBasedIndex.FileTypeSpecificInputFilter { 12 | @Override 13 | public void registerFileTypesUsedForIndexing(@NotNull Consumer fileTypeSink) { 14 | fileTypeSink.consume(PythonFileType.INSTANCE); 15 | } 16 | 17 | @Override 18 | public boolean acceptInput(@NotNull VirtualFile file) { 19 | return OdooModuleUtils.isInOdooModule(file); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiElementResolveResult; 7 | import com.intellij.psi.PsiReferenceBase; 8 | import com.intellij.psi.ResolveResult; 9 | import com.intellij.util.PlatformIcons; 10 | import com.jetbrains.python.psi.PyClass; 11 | import com.jetbrains.python.psi.PyUtil; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Collection; 15 | import java.util.LinkedList; 16 | import java.util.List; 17 | 18 | public class OdooModelReference extends PsiReferenceBase.Poly { 19 | public OdooModelReference(@NotNull PsiElement element) { 20 | super(element); 21 | } 22 | 23 | @Override 24 | public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) { 25 | return PsiElementResolveResult.createResults(resolveInner()); 26 | } 27 | 28 | @NotNull 29 | protected List resolveInner() { 30 | return PyUtil.getParameterizedCachedValue(getElement(), null, param -> { 31 | OdooModelClass modelClass = OdooModelClass.getInstance(getValue(), myElement.getProject()); 32 | List elements = new LinkedList<>(); 33 | List classes = OdooModelIndex.getAvailableOdooModelClassesByName(getValue(), getElement()); 34 | for (PyClass cls : classes) { 35 | elements.add(modelClass.bindWithElement(cls)); 36 | } 37 | return elements; 38 | }); 39 | } 40 | 41 | @Override 42 | public Object @NotNull [] getVariants() { 43 | List elements = new LinkedList<>(); 44 | Collection models = OdooModelIndex.getAvailableOdooModels(getElement()); 45 | models.forEach(model -> { 46 | LookupElement element = LookupElementBuilder.create(model).withIcon(PlatformIcons.CLASS_ICON); 47 | elements.add(element); 48 | }); 49 | return elements.toArray(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.PsiReferenceProvider; 6 | import com.intellij.util.ProcessingContext; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class OdooModelReferenceProvider extends PsiReferenceProvider { 10 | @Override 11 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 12 | @NotNull ProcessingContext context) { 13 | return new PsiReference[]{new OdooModelReference(element)}; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelRenameInputValidator.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.patterns.ElementPattern; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.refactoring.rename.RenameInputValidator; 7 | import com.intellij.util.ProcessingContext; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.regex.Pattern; 11 | 12 | public class OdooModelRenameInputValidator implements RenameInputValidator { 13 | @Override 14 | @NotNull 15 | public ElementPattern getPattern() { 16 | return PlatformPatterns.psiElement(OdooModelClass.class); 17 | } 18 | 19 | @Override 20 | public boolean isInputValid(@NotNull String newName, @NotNull PsiElement element, @NotNull ProcessingContext context) { 21 | return Pattern.matches("^[a-z0-9_.]+$", newName); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelSelfTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.jetbrains.python.psi.PyFunction; 5 | import com.jetbrains.python.psi.PyNamedParameter; 6 | import com.jetbrains.python.psi.types.PyType; 7 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 8 | import com.jetbrains.python.psi.types.TypeEvalContext; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class OdooModelSelfTypeProvider extends PyTypeProviderBase { 13 | @Nullable 14 | @Override 15 | public Ref getParameterType(@NotNull PyNamedParameter param, 16 | @NotNull PyFunction function, 17 | @NotNull TypeEvalContext context) { 18 | if (param.isSelf()) { 19 | OdooModelClass modelClass = OdooModelUtils.getContainingOdooModelClass(function); 20 | if (modelClass != null) { 21 | final PyFunction.Modifier modifier = function.getModifier(); 22 | OdooRecordSetType recordSetType = 23 | modifier == PyFunction.Modifier.CLASSMETHOD ? OdooRecordSetType.NONE : OdooRecordSetType.MULTI; 24 | OdooModelClassType type = new OdooModelClassType(modelClass, recordSetType); 25 | return Ref.create(type); 26 | } 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelStructureViewFactory.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.ide.structureView.StructureViewBuilder; 4 | import com.intellij.ide.structureView.StructureViewModel; 5 | import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; 6 | import com.intellij.lang.PsiStructureViewFactory; 7 | import com.intellij.openapi.editor.Editor; 8 | import com.intellij.psi.PsiFile; 9 | import com.jetbrains.python.psi.PyFile; 10 | import com.jetbrains.python.structureView.PyStructureViewModel; 11 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | public class OdooModelStructureViewFactory implements PsiStructureViewFactory { 16 | @Nullable 17 | @Override 18 | public StructureViewBuilder getStructureViewBuilder(@NotNull PsiFile psiFile) { 19 | return new TreeBasedStructureViewBuilder() { 20 | @NotNull 21 | @Override 22 | public StructureViewModel createStructureViewModel(@Nullable Editor editor) { 23 | if (psiFile instanceof PyFile && OdooModuleUtils.isInOdooModule(psiFile)) { 24 | return new OdooModelStructureViewModel(psiFile, editor); 25 | } 26 | return new PyStructureViewModel(psiFile, editor); 27 | } 28 | 29 | @Override 30 | public boolean isRootNodeShown() { 31 | return false; 32 | } 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelStructureViewModel.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.ide.util.treeView.smartTree.Sorter; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.psi.PsiFile; 6 | import com.jetbrains.python.psi.PyClass; 7 | import com.jetbrains.python.psi.PyElement; 8 | import com.jetbrains.python.psi.PyFunction; 9 | import com.jetbrains.python.structureView.PyStructureViewModel; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class OdooModelStructureViewModel extends PyStructureViewModel { 14 | public OdooModelStructureViewModel(@NotNull PsiFile psiFile, 15 | @Nullable Editor editor) { 16 | super(psiFile, editor, new OdooModelStructureViewElement((PyElement) psiFile)); 17 | withSorters(Sorter.ALPHA_SORTER); 18 | withSuitableClasses(PyFunction.class, PyClass.class); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooModelSuperMethodsSearch.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.util.Processor; 5 | import com.intellij.util.QueryExecutor; 6 | import com.jetbrains.python.psi.PyClass; 7 | import com.jetbrains.python.psi.PyFunction; 8 | import com.jetbrains.python.psi.search.PySuperMethodsSearch; 9 | import com.jetbrains.python.psi.types.TypeEvalContext; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.List; 13 | 14 | public class OdooModelSuperMethodsSearch implements QueryExecutor { 15 | @Override 16 | public boolean execute(@NotNull PySuperMethodsSearch.SearchParameters queryParameters, 17 | @NotNull Processor consumer) { 18 | final PyFunction func = queryParameters.getDerivedMethod(); 19 | final String name = func.getName(); 20 | if (name == null) { 21 | return false; 22 | } 23 | final PyClass containingClass = func.getContainingClass(); 24 | if (containingClass == null) { 25 | return false; 26 | } 27 | OdooModelClass modelClass = OdooModelUtils.getContainingOdooModelClass(containingClass); 28 | if (modelClass == null) { 29 | return true; 30 | } 31 | final TypeEvalContext context = queryParameters.getContext(); 32 | List methods = modelClass.multiFindMethodByName(name, true, context); 33 | methods.remove(func); 34 | for (PyFunction method : methods) { 35 | consumer.process(method); 36 | } 37 | return false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooRecordInSelfTypeProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.openapi.util.Ref; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.python.psi.PyExpression; 6 | import com.jetbrains.python.psi.PyForPart; 7 | import com.jetbrains.python.psi.PyTargetExpression; 8 | import com.jetbrains.python.psi.types.PyType; 9 | import com.jetbrains.python.psi.types.PyTypeProviderBase; 10 | import com.jetbrains.python.psi.types.TypeEvalContext; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | public class OdooRecordInSelfTypeProvider extends PyTypeProviderBase { 15 | @Override 16 | public Ref getReferenceType(@NotNull PsiElement element, 17 | @NotNull TypeEvalContext context, 18 | @Nullable PsiElement anchor) { 19 | if (element instanceof PyTargetExpression && element.getParent() instanceof PyForPart) { 20 | PyExpression source = ((PyForPart) element.getParent()).getSource(); 21 | if (source != null) { 22 | PyType type = context.getType(source); 23 | if (type instanceof OdooModelClassType) { 24 | type = ((OdooModelClassType) type).withOneRecord(); 25 | return Ref.create(type); 26 | } 27 | } 28 | } 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooRecordSetType.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | public enum OdooRecordSetType { 4 | NONE, 5 | MODEL, 6 | ONE, 7 | MULTI, 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/model/OdooSearchDomainFieldReferenceResolveProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.model; 2 | 3 | import com.intellij.openapi.util.Computable; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.python.psi.PyQualifiedExpression; 6 | import com.jetbrains.python.psi.resolve.PyReferenceResolveProvider; 7 | import com.jetbrains.python.psi.resolve.RatedResolveResult; 8 | import com.jetbrains.python.psi.types.TypeEvalContext; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class OdooSearchDomainFieldReferenceResolveProvider implements PyReferenceResolveProvider { 15 | @Override 16 | @NotNull 17 | public List resolveName(@NotNull PyQualifiedExpression pyQualifiedExpression, 18 | @NotNull TypeEvalContext typeEvalContext) { 19 | String name = pyQualifiedExpression.getReferencedName(); 20 | if (name == null || pyQualifiedExpression.getQualifier() != null) { 21 | return Collections.emptyList(); 22 | } 23 | Computable modelClassResolver = OdooModelUtils.getSearchDomainContextResolver(pyQualifiedExpression, false); 24 | if (modelClassResolver != null) { 25 | OdooModelClass modelClass = modelClassResolver.compute(); 26 | if (modelClass != null) { 27 | PsiElement field = modelClass.findField(name, typeEvalContext); 28 | if (field != null) { 29 | return Collections.singletonList(new RatedResolveResult(RatedResolveResult.RATE_NORMAL, field)); 30 | } 31 | } 32 | } 33 | return Collections.emptyList(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/module/OdooAddonsImportResolver.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.module; 2 | 3 | import com.intellij.openapi.project.DumbService; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.util.QualifiedName; 7 | import com.jetbrains.python.psi.impl.PyImportResolver; 8 | import com.jetbrains.python.psi.resolve.PyQualifiedNameResolveContext; 9 | import com.jetbrains.python.psi.resolve.PyResolveImportUtil; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.List; 14 | 15 | public class OdooAddonsImportResolver implements PyImportResolver { 16 | @Nullable 17 | @Override 18 | public PsiElement resolveImportReference(@NotNull QualifiedName name, 19 | @NotNull PyQualifiedNameResolveContext context, 20 | boolean withRoot) { 21 | Project project = context.getProject(); 22 | if (DumbService.isDumb(project)) { 23 | return null; 24 | } 25 | List components = name.getComponents(); 26 | if (components.size() < 1 || !"odoo".equals(components.get(0))) { 27 | return null; 28 | } 29 | if (components.size() > 2 && "addons".equals(components.get(1))) { 30 | String moduleName = components.get(2); 31 | PsiElement foothold = context.getFoothold(); 32 | if (foothold == null) { 33 | return null; 34 | } 35 | OdooModule module = OdooModuleIndex.getOdooModuleByName(moduleName, foothold); 36 | if (module != null) { 37 | QualifiedName relatedName = name.subQualifiedName(3, name.getComponentCount()); 38 | List refs = PyResolveImportUtil.resolveModuleAt(relatedName, module.getDirectory(), context); 39 | if (!refs.isEmpty()) { 40 | return refs.get(0); 41 | } 42 | } 43 | } else { 44 | context = context.copyWithoutForeign().copyWithoutStubs(); 45 | List refs = PyResolveImportUtil.resolveQualifiedName(name, context); 46 | if (!refs.isEmpty()) { 47 | return refs.get(0); 48 | } 49 | } 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/module/OdooAddonsMembersProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.module; 2 | 3 | import com.jetbrains.python.codeInsight.PyCustomMember; 4 | import com.jetbrains.python.psi.PyFile; 5 | import com.jetbrains.python.psi.types.PyModuleMembersProvider; 6 | import com.jetbrains.python.psi.types.TypeEvalContext; 7 | import dev.ngocta.pycharm.odoo.OdooNames; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | 14 | public class OdooAddonsMembersProvider extends PyModuleMembersProvider { 15 | @NotNull 16 | @Override 17 | protected Collection getMembersByQName(@NotNull PyFile file, 18 | @NotNull String name, 19 | @NotNull TypeEvalContext context) { 20 | if (OdooNames.ODOO_ADDONS_QNAME.equals(name)) { 21 | Collection modules = OdooModuleIndex.getAvailableOdooModules(file); 22 | Collection members = new ArrayList<>(); 23 | for (OdooModule module : modules) { 24 | PyCustomMember member = new PyCustomMember(module.getName(), module.getDirectory()); 25 | members.add(member); 26 | } 27 | return members; 28 | } 29 | return Collections.emptyList(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/module/OdooModuleDocumentationProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.module; 2 | 3 | import com.intellij.lang.documentation.DocumentationMarkup; 4 | import com.intellij.lang.documentation.DocumentationProvider; 5 | import com.intellij.psi.PsiDirectory; 6 | import com.intellij.psi.PsiElement; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class OdooModuleDocumentationProvider implements DocumentationProvider { 10 | @Nullable 11 | @Override 12 | public String generateDoc(PsiElement element, 13 | @Nullable PsiElement originalElement) { 14 | if (element instanceof PsiDirectory) { 15 | PsiDirectory dir = (PsiDirectory) element; 16 | if (OdooModuleUtils.isOdooModuleDirectory(dir.getVirtualFile())) { 17 | OdooModule module = new OdooModule(dir); 18 | OdooManifestInfo info = module.getManifestInfo(); 19 | if (info == null || info.getName() == null) { 20 | return null; 21 | } 22 | String doc = DocumentationMarkup.DEFINITION_START + info.getName() + DocumentationMarkup.DEFINITION_END; 23 | if (info.getSummary() != null && !info.getSummary().isEmpty()) { 24 | doc += DocumentationMarkup.CONTENT_START + info.getSummary() + DocumentationMarkup.CONTENT_END; 25 | } 26 | return doc; 27 | } 28 | } 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/module/OdooModuleHookFunctionReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.module; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiFile; 5 | import com.intellij.psi.PsiReference; 6 | import com.intellij.psi.PsiReferenceBase; 7 | import com.intellij.util.ObjectUtils; 8 | import com.jetbrains.python.PyNames; 9 | import com.jetbrains.python.psi.PyFile; 10 | import com.jetbrains.python.psi.PyUtil; 11 | import com.jetbrains.python.psi.resolve.RatedResolveResult; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.List; 16 | 17 | public class OdooModuleHookFunctionReference extends PsiReferenceBase implements PsiReference { 18 | public OdooModuleHookFunctionReference(@NotNull PsiElement element) { 19 | super(element); 20 | } 21 | 22 | @Override 23 | @Nullable 24 | public PsiElement resolve() { 25 | return PyUtil.getNullableParameterizedCachedValue(getElement(), null, param -> { 26 | PyFile initFile = getInitFile(); 27 | if (initFile == null) { 28 | return null; 29 | } 30 | List resolveResults = initFile.multiResolveName(getValue(), true); 31 | for (RatedResolveResult resolveResult : resolveResults) { 32 | return resolveResult.getElement(); 33 | } 34 | return null; 35 | }); 36 | } 37 | 38 | @Override 39 | public Object @NotNull [] getVariants() { 40 | PyFile file = getInitFile(); 41 | if (file != null) { 42 | return file.getTopLevelFunctions().toArray(); 43 | } 44 | return new Object[0]; 45 | } 46 | 47 | @Nullable 48 | private PyFile getInitFile() { 49 | OdooModule module = OdooModuleUtils.getContainingOdooModule(getElement()); 50 | if (module == null) { 51 | return null; 52 | } 53 | PsiFile file = module.getDirectory().findFile(PyNames.INIT_DOT_PY); 54 | return ObjectUtils.tryCast(file, PyFile.class); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/module/OdooModuleHookFunctionReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.module; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.PsiReferenceProvider; 6 | import com.intellij.util.ProcessingContext; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class OdooModuleHookFunctionReferenceProvider extends PsiReferenceProvider { 10 | @Override 11 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 12 | @NotNull ProcessingContext context) { 13 | return new PsiReference[]{new OdooModuleHookFunctionReference(element)}; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/module/OdooModuleReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.module; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReferenceBase; 5 | import com.jetbrains.python.psi.PyUtil; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.Collection; 10 | 11 | public class OdooModuleReference extends PsiReferenceBase { 12 | public OdooModuleReference(@NotNull PsiElement element) { 13 | super(element); 14 | } 15 | 16 | @Nullable 17 | @Override 18 | public PsiElement resolve() { 19 | return PyUtil.getNullableParameterizedCachedValue(getElement(), null, param -> { 20 | OdooModule module = OdooModuleIndex.getOdooModuleByName(getValue(), getElement()); 21 | return module != null ? module.getDirectory() : null; 22 | }); 23 | } 24 | 25 | @Override 26 | public Object @NotNull [] getVariants() { 27 | Collection modules = OdooModuleIndex.getAvailableOdooModules(getElement()); 28 | OdooModule module = OdooModuleUtils.getContainingOdooModule(getElement()); 29 | if (module != null) { 30 | modules.remove(module); 31 | } 32 | return modules.stream().map(OdooModule::getDirectory).toArray(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/module/OdooModuleReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.module; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.PsiReferenceProvider; 6 | import com.intellij.util.ProcessingContext; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class OdooModuleReferenceProvider extends PsiReferenceProvider { 10 | @Override 11 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 12 | @NotNull ProcessingContext context) { 13 | return new PsiReference[]{new OdooModuleReference(element)}; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/psi/OdooPyNamedParameter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.jetbrains.python.psi.impl.PyNamedParameterImpl; 5 | import com.jetbrains.python.psi.types.PyType; 6 | import com.jetbrains.python.psi.types.TypeEvalContext; 7 | import dev.ngocta.pycharm.odoo.python.model.OdooModelUtils; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public class OdooPyNamedParameter extends PyNamedParameterImpl { 12 | public OdooPyNamedParameter(@NotNull ASTNode node) { 13 | super(node); 14 | } 15 | 16 | @Override 17 | public @Nullable PyType getType(@NotNull TypeEvalContext context, 18 | TypeEvalContext.@NotNull Key key) { 19 | PyType type = super.getType(context, key); 20 | type = OdooModelUtils.upgradeModelType(type); 21 | return type; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/psi/OdooPyReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.psi; 2 | 3 | import com.intellij.openapi.util.Computable; 4 | import com.intellij.psi.PsiFile; 5 | import com.jetbrains.python.psi.PyQualifiedExpression; 6 | import com.jetbrains.python.psi.impl.references.PyReferenceImpl; 7 | import com.jetbrains.python.psi.resolve.PyResolveContext; 8 | import com.jetbrains.python.psi.resolve.RatedResolveResult; 9 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClass; 10 | import dev.ngocta.pycharm.odoo.python.model.OdooModelUtils; 11 | import dev.ngocta.pycharm.odoo.xml.OdooXmlUtils; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | public class OdooPyReference extends PyReferenceImpl { 18 | public OdooPyReference(PyQualifiedExpression element, 19 | @NotNull PyResolveContext context) { 20 | super(element, context); 21 | } 22 | 23 | @Override 24 | @NotNull 25 | protected List resolveInner() { 26 | PsiFile file = myContext.getTypeEvalContext().getOrigin(); 27 | if (OdooXmlUtils.isOdooXmlDataElement(file)) { 28 | return Collections.emptyList(); 29 | } 30 | return super.resolveInner(); 31 | } 32 | 33 | @Override 34 | public Object @NotNull [] getVariants() { 35 | Object[] variants = getSearchDomainFieldVariants(); 36 | if (variants != null) { 37 | return variants; 38 | } 39 | return super.getVariants(); 40 | } 41 | 42 | private Object[] getSearchDomainFieldVariants() { 43 | PsiFile file = getElement().getContainingFile(); 44 | if (file == null || file.getContext() == null) { 45 | return null; 46 | } 47 | Computable modelClassResolver = OdooModelUtils.getSearchDomainContextResolver(getElement(), false); 48 | if (modelClassResolver != null) { 49 | OdooModelClass modelClass = modelClassResolver.compute(); 50 | if (modelClass != null) { 51 | return OdooModelUtils.getFieldLookupElements(modelClass, myContext.getTypeEvalContext()); 52 | } else { 53 | return OdooModelUtils.getImplicitFieldLookupElements(getElement()); 54 | } 55 | } 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/psi/OdooPyReferenceExpression.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiPolyVariantReference; 5 | import com.jetbrains.python.psi.impl.PyReferenceExpressionImpl; 6 | import com.jetbrains.python.psi.impl.references.PyImportReference; 7 | import com.jetbrains.python.psi.impl.references.PyQualifiedReference; 8 | import com.jetbrains.python.psi.impl.references.PyReferenceImpl; 9 | import com.jetbrains.python.psi.resolve.PyResolveContext; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class OdooPyReferenceExpression extends PyReferenceExpressionImpl { 13 | public OdooPyReferenceExpression(@NotNull ASTNode astNode) { 14 | super(astNode); 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public PsiPolyVariantReference getReference(@NotNull PyResolveContext context) { 20 | PsiPolyVariantReference reference = super.getReference(context); 21 | if (reference instanceof PyImportReference) { 22 | return reference; 23 | } else if (reference instanceof PyQualifiedReference) { 24 | reference = new OdooPyQualifiedReference(this, context); 25 | } else if (reference instanceof PyReferenceImpl) { 26 | reference = new OdooPyReference(this, context); 27 | } 28 | return reference; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/psi/OdooPySliceExpression.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.jetbrains.python.psi.PyExpression; 5 | import com.jetbrains.python.psi.impl.PySliceExpressionImpl; 6 | import com.jetbrains.python.psi.types.PyType; 7 | import com.jetbrains.python.psi.types.TypeEvalContext; 8 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClassType; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class OdooPySliceExpression extends PySliceExpressionImpl { 13 | public OdooPySliceExpression(ASTNode astNode) { 14 | super(astNode); 15 | } 16 | 17 | @Nullable 18 | @Override 19 | public PyType getType(@NotNull TypeEvalContext context, 20 | @NotNull TypeEvalContext.Key key) { 21 | PyExpression operand = getOperand(); 22 | PyType operandType = context.getType(operand); 23 | if (operandType instanceof OdooModelClassType) { 24 | return operandType; 25 | } 26 | return super.getType(context, key); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/psi/OdooPyStringLiteralExpression.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.jetbrains.python.psi.PyStringLiteralUtil; 5 | import com.jetbrains.python.psi.impl.PyBuiltinCache; 6 | import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl; 7 | import com.jetbrains.python.psi.types.PyType; 8 | import com.jetbrains.python.psi.types.TypeEvalContext; 9 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class OdooPyStringLiteralExpression extends PyStringLiteralExpressionImpl { 13 | public OdooPyStringLiteralExpression(ASTNode astNode) { 14 | super(astNode); 15 | } 16 | 17 | @Override 18 | public PyType getType(@NotNull TypeEvalContext context, 19 | @NotNull TypeEvalContext.Key key) { 20 | if (OdooModuleUtils.isInOdooModule(context.getOrigin())) { 21 | String text = getText(); 22 | if (!text.isEmpty() && !PyStringLiteralUtil.isBytesPrefix(text.substring(0, 1))) { 23 | final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(context.getOrigin() == null ? this : context.getOrigin()); 24 | return builtinCache.getStrType(); 25 | } 26 | } 27 | return super.getType(context, key); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/psi/OdooPySubscriptionExpression.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.python.psi.PyExpression; 6 | import com.jetbrains.python.psi.PyNumericLiteralExpression; 7 | import com.jetbrains.python.psi.PyStringLiteralExpression; 8 | import com.jetbrains.python.psi.impl.PySubscriptionExpressionImpl; 9 | import com.jetbrains.python.psi.types.PyType; 10 | import com.jetbrains.python.psi.types.TypeEvalContext; 11 | import dev.ngocta.pycharm.odoo.python.model.OdooFieldInfo; 12 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClassType; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | public class OdooPySubscriptionExpression extends PySubscriptionExpressionImpl { 17 | public OdooPySubscriptionExpression(ASTNode astNode) { 18 | super(astNode); 19 | } 20 | 21 | @Nullable 22 | @Override 23 | public PyType getType(@NotNull TypeEvalContext context, 24 | @NotNull TypeEvalContext.Key key) { 25 | PyExpression operand = getOperand(); 26 | PyType operandType = context.getType(operand); 27 | if (operandType instanceof OdooModelClassType) { 28 | OdooModelClassType modelClassType = (OdooModelClassType) operandType; 29 | PyExpression index = getIndexExpression(); 30 | if (index instanceof PyStringLiteralExpression) { 31 | String fieldName = ((PyStringLiteralExpression) index).getStringValue(); 32 | PsiElement field = modelClassType.resolvePsiMember(fieldName, context); 33 | return OdooFieldInfo.getFieldType(field, context); 34 | } 35 | if (index instanceof PyNumericLiteralExpression) { 36 | return modelClassType.withOneRecord(); 37 | } 38 | return null; 39 | } 40 | return super.getType(context, key); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/python/psi/OdooPyTargetExpression.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.python.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.jetbrains.python.psi.impl.PyPsiUtils; 5 | import com.jetbrains.python.psi.impl.PyTargetExpressionImpl; 6 | import com.jetbrains.python.psi.types.PyType; 7 | import com.jetbrains.python.psi.types.TypeEvalContext; 8 | import dev.ngocta.pycharm.odoo.python.model.OdooModelUtils; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class OdooPyTargetExpression extends PyTargetExpressionImpl { 12 | public OdooPyTargetExpression(@NotNull ASTNode node) { 13 | super(node); 14 | } 15 | 16 | @Override 17 | public PyType getType(@NotNull TypeEvalContext context, 18 | TypeEvalContext.@NotNull Key key) { 19 | PyType type = super.getType(context, key); 20 | if (PyPsiUtils.isMethodContext(this)) { 21 | return OdooModelUtils.upgradeModelType(type); 22 | } 23 | return type; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooCssClassInXmlReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.patterns.XmlAttributeValuePattern; 4 | import com.intellij.patterns.XmlPatterns; 5 | import com.intellij.psi.PsiReferenceContributor; 6 | import com.intellij.psi.PsiReferenceRegistrar; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class OdooCssClassInXmlReferenceContributor extends PsiReferenceContributor { 10 | public static final XmlAttributeValuePattern CLASS_PATTERN = 11 | XmlPatterns.xmlAttributeValue("class", "icon") 12 | .with(OdooXmlUtils.ODOO_XML_ELEMENT_PATTERN_CONDITION); 13 | 14 | @Override 15 | public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { 16 | OdooCssClassInXmlReferenceProvider provider = new OdooCssClassInXmlReferenceProvider(); 17 | registrar.registerReferenceProvider(CLASS_PATTERN, provider); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooCssClassInXmlReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.openapi.util.TextRange; 4 | import com.intellij.psi.ElementManipulators; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiReference; 7 | import com.intellij.psi.PsiReferenceProvider; 8 | import com.intellij.psi.css.util.CssResolveUtil; 9 | import com.intellij.psi.xml.XmlAttributeValue; 10 | import com.intellij.util.ProcessingContext; 11 | import dev.ngocta.pycharm.odoo.css.OdooCssClassReference; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.LinkedList; 15 | import java.util.List; 16 | 17 | public class OdooCssClassInXmlReferenceProvider extends PsiReferenceProvider { 18 | 19 | @Override 20 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 21 | @NotNull ProcessingContext context) { 22 | List references = new LinkedList<>(); 23 | if (element instanceof XmlAttributeValue) { 24 | String value = ((XmlAttributeValue) element).getValue(); 25 | TextRange baseRange = ElementManipulators.getValueTextRange(element); 26 | CssResolveUtil.consumeClassNames(value, element, (name, range) -> { 27 | references.add(new OdooCssClassReference(element, range.shiftRight(baseRange.getStartOffset()), true)); 28 | }); 29 | } 30 | return references.toArray(PsiReference.EMPTY_ARRAY); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooFieldWidgetInXmlReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.patterns.PatternCondition; 4 | import com.intellij.patterns.XmlAttributeValuePattern; 5 | import com.intellij.patterns.XmlPatterns; 6 | import com.intellij.psi.PsiReferenceContributor; 7 | import com.intellij.psi.PsiReferenceProvider; 8 | import com.intellij.psi.PsiReferenceRegistrar; 9 | import com.intellij.psi.xml.XmlAttributeValue; 10 | import com.intellij.util.ProcessingContext; 11 | import com.intellij.util.xml.DomElement; 12 | import com.intellij.util.xml.DomUtil; 13 | import dev.ngocta.pycharm.odoo.javascript.OdooJSFieldWidgetReferenceProvider; 14 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomViewField; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | public class OdooFieldWidgetInXmlReferenceContributor extends PsiReferenceContributor { 18 | public static final XmlAttributeValuePattern XML_FIELD_WIDGET_ATTR_PATTERN = 19 | XmlPatterns.xmlAttributeValue("widget").with(new PatternCondition("") { 20 | @Override 21 | public boolean accepts(@NotNull XmlAttributeValue xmlAttributeValue, ProcessingContext context) { 22 | DomElement domElement = DomUtil.getDomElement(xmlAttributeValue); 23 | return domElement instanceof OdooDomViewField; 24 | } 25 | }); 26 | 27 | @Override 28 | public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { 29 | PsiReferenceProvider provider = new OdooJSFieldWidgetReferenceProvider(); 30 | registrar.registerReferenceProvider(XML_FIELD_WIDGET_ATTR_PATTERN, provider); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooJSTemplateElement.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.psi.PsiTarget; 4 | import com.intellij.psi.impl.PomTargetPsiElementImpl; 5 | import com.intellij.util.PlatformIcons; 6 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import javax.swing.*; 10 | 11 | public class OdooJSTemplateElement extends PomTargetPsiElementImpl { 12 | public OdooJSTemplateElement(@NotNull PsiTarget target) { 13 | super(target); 14 | } 15 | 16 | @Override 17 | public String getLocationString() { 18 | return OdooModuleUtils.getLocationStringForFile(getContainingFile()); 19 | } 20 | 21 | @Override 22 | public Icon getIcon() { 23 | return PlatformIcons.XML_TAG_ICON; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooJSTemplateFindUsageProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.lang.findUsages.FindUsagesProvider; 4 | import com.intellij.pom.PomTarget; 5 | import com.intellij.pom.PomTargetPsiElement; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.util.xml.DomTarget; 8 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomJSTemplate; 9 | import org.jetbrains.annotations.Nls; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class OdooJSTemplateFindUsageProvider implements FindUsagesProvider { 14 | @Override 15 | public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { 16 | if (psiElement instanceof PomTargetPsiElement) { 17 | PomTarget target = ((PomTargetPsiElement) psiElement).getTarget(); 18 | if (target instanceof DomTarget) { 19 | return ((DomTarget) target).getDomElement() instanceof OdooDomJSTemplate; 20 | } 21 | } 22 | return false; 23 | } 24 | 25 | @Override 26 | @Nullable 27 | public String getHelpId(@NotNull PsiElement psiElement) { 28 | return null; 29 | } 30 | 31 | @Override 32 | @Nls 33 | @NotNull 34 | public String getType(@NotNull PsiElement element) { 35 | return ""; 36 | } 37 | 38 | @Override 39 | @Nls 40 | @NotNull 41 | public String getDescriptiveName(@NotNull PsiElement element) { 42 | return ""; 43 | } 44 | 45 | @Override 46 | @Nls 47 | @NotNull 48 | public String getNodeText(@NotNull PsiElement element, 49 | boolean useFullName) { 50 | return ""; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooJSTemplateGotoContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.navigation.ChooseByNameContributorEx; 4 | import com.intellij.navigation.NavigationItem; 5 | import com.intellij.psi.search.GlobalSearchScope; 6 | import com.intellij.util.Processor; 7 | import com.intellij.util.indexing.FindSymbolParameters; 8 | import com.intellij.util.indexing.IdFilter; 9 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomJSTemplate; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.List; 14 | 15 | public class OdooJSTemplateGotoContributor implements ChooseByNameContributorEx { 16 | @Override 17 | public void processNames(@NotNull Processor processor, 18 | @NotNull GlobalSearchScope scope, 19 | @Nullable IdFilter filter) { 20 | if (scope.getProject() != null) { 21 | OdooJSTemplateIndex.processAvailableTemplateNames(scope, scope.getProject(), processor::process, false); 22 | } 23 | } 24 | 25 | @Override 26 | public void processElementsWithName(@NotNull String name, 27 | @NotNull Processor processor, 28 | @NotNull FindSymbolParameters parameters) { 29 | List templates = OdooJSTemplateIndex.findTemplatesByName(name, parameters.getSearchScope(), parameters.getProject(), false); 30 | for (OdooDomJSTemplate template : templates) { 31 | OdooJSTemplateElement element = template.getNavigationElement(); 32 | if (element != null) { 33 | processor.process(element); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooJSTemplateReference.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiElementResolveResult; 7 | import com.intellij.psi.PsiReferenceBase; 8 | import com.intellij.psi.ResolveResult; 9 | import com.intellij.util.PlatformIcons; 10 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomJSTemplate; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.LinkedList; 14 | import java.util.List; 15 | 16 | public class OdooJSTemplateReference extends PsiReferenceBase.Poly { 17 | private final boolean myIsQualified; 18 | 19 | public OdooJSTemplateReference(PsiElement psiElement, 20 | boolean isQualified) { 21 | super(psiElement); 22 | myIsQualified = isQualified; 23 | } 24 | 25 | @Override 26 | public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) { 27 | String name = getValue(); 28 | List elements = new LinkedList<>(); 29 | List templates = OdooJSTemplateIndex.findTemplatesByName(name, getElement(), myIsQualified); 30 | for (OdooDomJSTemplate t : templates) { 31 | OdooJSTemplateElement element = t.getNavigationElement(); 32 | if (element != null) { 33 | elements.add(element); 34 | } 35 | } 36 | return PsiElementResolveResult.createResults(elements); 37 | } 38 | 39 | @Override 40 | public Object @NotNull [] getVariants() { 41 | List variants = new LinkedList<>(); 42 | List names = OdooJSTemplateIndex.getAvailableTemplateNames(getElement(), myIsQualified); 43 | for (String name : names) { 44 | LookupElement lookupElement = LookupElementBuilder.create(name).withIcon(PlatformIcons.XML_TAG_ICON); 45 | variants.add(lookupElement); 46 | } 47 | return variants.toArray(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooJSTemplateReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.patterns.PatternCondition; 4 | import com.intellij.patterns.XmlAttributeValuePattern; 5 | import com.intellij.patterns.XmlPatterns; 6 | import com.intellij.psi.PsiReferenceContributor; 7 | import com.intellij.psi.PsiReferenceRegistrar; 8 | import com.intellij.psi.xml.XmlAttributeValue; 9 | import com.intellij.util.ProcessingContext; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class OdooJSTemplateReferenceContributor extends PsiReferenceContributor { 13 | public static final XmlAttributeValuePattern T_PATTERN = 14 | XmlPatterns.xmlAttributeValue("t-inherit", "t-extend", "t-call") 15 | .with(OdooXmlUtils.ODOO_JS_TEMPLATE_ELEMENT_PATTERN_CONDITION) 16 | .with(new PatternCondition("") { 17 | @Override 18 | public boolean accepts(@NotNull XmlAttributeValue xmlAttributeValue, 19 | ProcessingContext context) { 20 | String attr = XmlAttributeValuePattern.getLocalName(xmlAttributeValue); 21 | if ("t-inherit".equals(attr)) { 22 | context.put(OdooJSTemplateReferenceProvider.IS_QUALIFIED, true); 23 | } 24 | return true; 25 | } 26 | }); 27 | 28 | @Override 29 | public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { 30 | OdooJSTemplateReferenceProvider provider = new OdooJSTemplateReferenceProvider(); 31 | registrar.registerReferenceProvider(T_PATTERN, provider); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooJSTemplateReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.openapi.util.Key; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiReference; 6 | import com.intellij.psi.PsiReferenceProvider; 7 | import com.intellij.util.ObjectUtils; 8 | import com.intellij.util.ProcessingContext; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class OdooJSTemplateReferenceProvider extends PsiReferenceProvider { 12 | public static final Key IS_QUALIFIED = new Key<>("isQualified"); 13 | 14 | @Override 15 | public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, 16 | @NotNull ProcessingContext context) { 17 | boolean isQualifier = ObjectUtils.notNull(context.get(IS_QUALIFIED), false); 18 | return new PsiReference[]{new OdooJSTemplateReference(element, isQualifier)}; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooXmlElementDescriptionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.psi.ElementDescriptionLocation; 4 | import com.intellij.psi.ElementDescriptionProvider; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.xml.XmlTag; 7 | import com.intellij.usageView.UsageViewTypeLocation; 8 | import com.intellij.util.xml.DomElement; 9 | import com.intellij.util.xml.DomManager; 10 | import com.intellij.util.xml.ElementPresentation; 11 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomElement; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | public class OdooXmlElementDescriptionProvider implements ElementDescriptionProvider { 16 | @Override 17 | @Nullable 18 | public String getElementDescription(@NotNull PsiElement element, 19 | @NotNull ElementDescriptionLocation location) { 20 | if (element instanceof XmlTag) { 21 | DomElement domElement = DomManager.getDomManager(element.getProject()).getDomElement((XmlTag) element); 22 | if (domElement instanceof OdooDomElement) { 23 | ElementPresentation presentation = domElement.getPresentation(); 24 | if (location instanceof UsageViewTypeLocation) { 25 | return presentation.getTypeName(); 26 | } 27 | } 28 | } 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooXmlFormatter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.formatting.CustomFormattingModelBuilder; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.lang.xml.XmlFormattingModelBuilder; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.codeStyle.CodeStyleSettings; 8 | import com.intellij.psi.formatter.FormattingDocumentModelImpl; 9 | import com.intellij.psi.formatter.xml.XmlBlock; 10 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 11 | 12 | public class OdooXmlFormatter extends XmlFormattingModelBuilder implements CustomFormattingModelBuilder { 13 | @Override 14 | public boolean isEngagedToFormat(PsiElement context) { 15 | return OdooModuleUtils.isInOdooModule(context); 16 | } 17 | 18 | @Override 19 | protected XmlBlock createBlock(CodeStyleSettings settings, 20 | ASTNode root, 21 | FormattingDocumentModelImpl documentModel) { 22 | return new XmlBlock(root, null, null, new OdooXmlPolicy(settings, documentModel), null, null, false); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooXmlInspection.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.util.xml.DomElement; 4 | import com.intellij.util.xml.highlighting.BasicDomElementsInspection; 5 | import com.intellij.util.xml.highlighting.DomElementAnnotationHolder; 6 | import com.intellij.util.xml.highlighting.DomHighlightingHelper; 7 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomElement; 8 | 9 | public class OdooXmlInspection extends BasicDomElementsInspection { 10 | public OdooXmlInspection() { 11 | super(OdooDomElement.class); 12 | } 13 | 14 | @Override 15 | protected void checkDomElement(DomElement element, 16 | DomElementAnnotationHolder holder, 17 | DomHighlightingHelper helper) { 18 | super.checkDomElement(element, holder, helper); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooXmlPolicy.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.formatting.FormattingDocumentModel; 4 | import com.intellij.formatting.WrapType; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiWhiteSpace; 7 | import com.intellij.psi.codeStyle.CodeStyleSettings; 8 | import com.intellij.psi.formatter.xml.XmlPolicy; 9 | import com.intellij.psi.xml.XmlElementType; 10 | import com.intellij.psi.xml.XmlTag; 11 | import com.intellij.psi.xml.XmlText; 12 | import com.intellij.util.xml.DomUtil; 13 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomViewElement; 14 | 15 | public class OdooXmlPolicy extends XmlPolicy { 16 | public OdooXmlPolicy(CodeStyleSettings settings, 17 | FormattingDocumentModel documentModel) { 18 | super(settings, documentModel); 19 | } 20 | 21 | @Override 22 | public WrapType getWrappingTypeForTagBegin(XmlTag tag) { 23 | if (DomUtil.findDomElement(tag, OdooDomViewElement.class) != null) { 24 | PsiElement prevSibling = tag.getPrevSibling(); 25 | if (prevSibling instanceof XmlText) { 26 | PsiElement[] children = prevSibling.getChildren(); 27 | if (children.length == 1 && children[0] instanceof PsiWhiteSpace) { 28 | prevSibling = prevSibling.getPrevSibling(); 29 | } 30 | } 31 | if (prevSibling.getNode().getElementType() != XmlElementType.XML_COMMENT) { 32 | return WrapType.NONE; 33 | } 34 | } 35 | return super.getWrappingTypeForTagBegin(tag); 36 | } 37 | 38 | @Override 39 | public WrapType getWrappingTypeForTagEnd(XmlTag xmlTag) { 40 | return WrapType.NONE; 41 | } 42 | 43 | @Override 44 | public boolean shouldSaveSpacesBetweenTagAndText() { 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooXmlStructureViewBuilderProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.ide.structureView.StructureViewBuilder; 4 | import com.intellij.ide.structureView.StructureViewModel; 5 | import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; 6 | import com.intellij.ide.structureView.impl.xml.XmlStructureViewTreeModel; 7 | import com.intellij.ide.structureView.xml.XmlStructureViewBuilderProvider; 8 | import com.intellij.openapi.editor.Editor; 9 | import com.intellij.psi.xml.XmlFile; 10 | import dev.ngocta.pycharm.odoo.xml.dom.OdooDomElement; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | public class OdooXmlStructureViewBuilderProvider implements XmlStructureViewBuilderProvider { 15 | @Nullable 16 | @Override 17 | public StructureViewBuilder createStructureViewBuilder(@NotNull XmlFile file) { 18 | // Own dom is not completed, fallback to default structure view 19 | if (OdooXmlUtils.getDomFile(file, OdooDomElement.class) != null) { 20 | return new TreeBasedStructureViewBuilder() { 21 | @Override 22 | @NotNull 23 | public StructureViewModel createStructureViewModel(@Nullable Editor editor) { 24 | return new XmlStructureViewTreeModel(file, editor); 25 | } 26 | }; 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/OdooXmlSuppressionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml; 2 | 3 | import com.intellij.codeInspection.XmlSuppressionProvider; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.psi.xml.XmlFile; 7 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class OdooXmlSuppressionProvider extends XmlSuppressionProvider { 11 | @Override 12 | public boolean isProviderAvailable(@NotNull PsiFile file) { 13 | return file instanceof XmlFile && OdooModuleUtils.isInOdooModule(file); 14 | } 15 | 16 | @Override 17 | public boolean isSuppressedFor(@NotNull PsiElement element, 18 | @NotNull String inspectionId) { 19 | return inspectionId.equals("CheckTagEmptyBody") || inspectionId.equals("CheckValidXmlInScriptTagBody"); 20 | } 21 | 22 | @Override 23 | public void suppressForFile(@NotNull PsiElement element, 24 | @NotNull String inspectionId) { 25 | 26 | } 27 | 28 | @Override 29 | public void suppressForTag(@NotNull PsiElement element, 30 | @NotNull String inspectionId) { 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomActWindow.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import dev.ngocta.pycharm.odoo.OdooNames; 4 | 5 | public interface OdooDomActWindow extends OdooDomRecordLike { 6 | @Override 7 | default String getModel() { 8 | return OdooNames.IR_ACTIONS_ACT_WINDOW; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomData.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | public interface OdooDomData extends OdooDomOperationContainer { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomDataFile.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.DomUtil; 4 | import com.intellij.util.xml.SubTagList; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | public interface OdooDomDataFile extends OdooDomOperationContainer { 10 | String NAME = "odoo"; 11 | 12 | @SubTagList("data") 13 | List getGroups(); 14 | 15 | default List getAllRecordLikeItems() { 16 | List result = new LinkedList<>(DomUtil.getChildrenOf(this, OdooDomRecordLike.class)); 17 | getGroups().forEach(group -> result.addAll(DomUtil.getChildrenOfType(group, OdooDomRecordLike.class))); 18 | return result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomDataFileMetaData.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.DomFileDescription; 4 | 5 | public class OdooDomDataFileMetaData extends DomFileDescription { 6 | public OdooDomDataFileMetaData() { 7 | super(OdooDomDataFile.class, OdooDomDataFile.NAME); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomElement.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.intellij.util.PlatformIcons; 5 | import com.intellij.util.xml.DomElement; 6 | import com.intellij.util.xml.DomUtil; 7 | import com.intellij.util.xml.ElementPresentation; 8 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 9 | import dev.ngocta.pycharm.odoo.python.module.OdooModuleUtils; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import javax.swing.*; 14 | 15 | public interface OdooDomElement extends DomElement { 16 | default PsiFile getFile() { 17 | return DomUtil.getFile(this); 18 | } 19 | 20 | default OdooModule getOdooModule() { 21 | return OdooModuleUtils.getContainingOdooModule(getFile()); 22 | } 23 | 24 | @Override 25 | @NotNull 26 | default ElementPresentation getPresentation() { 27 | return new ElementPresentation() { 28 | @Override 29 | public String getElementName() { 30 | return ""; 31 | } 32 | 33 | @Override 34 | public String getTypeName() { 35 | return ""; 36 | } 37 | 38 | @Override 39 | @Nullable 40 | public Icon getIcon() { 41 | return PlatformIcons.XML_TAG_ICON; 42 | } 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomField.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.Attribute; 6 | import com.intellij.util.xml.GenericAttributeValue; 7 | import com.intellij.util.xml.Referencing; 8 | import com.intellij.util.xml.Required; 9 | import dev.ngocta.pycharm.odoo.python.model.OdooFieldInfo; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.Optional; 13 | 14 | public interface OdooDomField extends OdooDomElement, OdooDomModelScoped { 15 | @Attribute("name") 16 | @Required 17 | @Referencing(OdooFieldNameReferenceConverter.class) 18 | GenericAttributeValue getNameAttr(); 19 | 20 | @Nullable 21 | default String getName() { 22 | return getNameAttr().getStringValue(); 23 | } 24 | 25 | @Nullable 26 | default String getComodel() { 27 | return Optional.ofNullable(getNameAttr()) 28 | .map(GenericAttributeValue::getXmlAttributeValue) 29 | .map(PsiElement::getReference) 30 | .map(PsiReference::resolve) 31 | .map(OdooFieldInfo::getInfo) 32 | .map(OdooFieldInfo::getComodel) 33 | .orElse(null); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomFieldAssignment.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.*; 6 | import com.jetbrains.python.psi.PyClass; 7 | import dev.ngocta.pycharm.odoo.data.OdooRecordElement; 8 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClass; 9 | import dev.ngocta.pycharm.odoo.python.model.OdooModelUtils; 10 | 11 | import java.util.Optional; 12 | 13 | @Referencing(OdooFieldValueReferenceConverter.class) 14 | public interface OdooDomFieldAssignment extends OdooDomField, GenericDomValue { 15 | @Attribute("ref") 16 | @Referencing(OdooFieldRefValueReferenceConverter.class) 17 | GenericAttributeValue getRefAttr(); 18 | 19 | @Attribute("file") 20 | @Referencing(OdooFieldFileValueReferenceConverter.class) 21 | GenericAttributeValue getFileAttr(); 22 | 23 | default String getModel() { 24 | OdooDomRecord record = getRecord(); 25 | if (record != null) { 26 | return record.getModel(); 27 | } 28 | return null; 29 | } 30 | 31 | default OdooDomRecord getRecord() { 32 | DomElement parent = getParent(); 33 | if (parent instanceof OdooDomRecord) { 34 | return (OdooDomRecord) parent; 35 | } 36 | return null; 37 | } 38 | 39 | default String getRefModel() { 40 | return Optional.of(getRefAttr()) 41 | .map(GenericAttributeValue::getXmlAttributeValue) 42 | .map(PsiElement::getReference) 43 | .map(PsiReference::resolve) 44 | .filter(OdooRecordElement.class::isInstance) 45 | .map(PsiElement::getNavigationElement) 46 | .filter(PyClass.class::isInstance) 47 | .map(OdooModelUtils::getContainingOdooModelClass) 48 | .map(OdooModelClass::getName) 49 | .orElse(null); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomJSTemplateFile.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.DomUtil; 4 | 5 | import java.util.List; 6 | 7 | public interface OdooDomJSTemplateFile extends OdooDomElement { 8 | default List getAllTemplates() { 9 | return DomUtil.getChildrenOf(this, OdooDomJSTemplate.class); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomJSTemplateFileMetaData.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.openapi.module.Module; 4 | import com.intellij.psi.xml.XmlFile; 5 | import com.intellij.psi.xml.XmlTag; 6 | import com.intellij.util.ArrayUtil; 7 | import com.intellij.util.xml.DomFileDescription; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public class OdooDomJSTemplateFileMetaData extends DomFileDescription { 12 | public OdooDomJSTemplateFileMetaData() { 13 | super(OdooDomJSTemplateFile.class, ""); 14 | } 15 | 16 | @Override 17 | public boolean acceptsOtherRootTagNames() { 18 | return true; 19 | } 20 | 21 | @Override 22 | public boolean isMyFile(@NotNull XmlFile file, 23 | @Nullable Module module) { 24 | XmlTag xmlTag = file.getRootTag(); 25 | return xmlTag != null && ArrayUtil.contains(xmlTag.getName(), "template", "templates"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomMenuItem.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.*; 6 | import dev.ngocta.pycharm.odoo.OdooNames; 7 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReference; 8 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilters; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public interface OdooDomMenuItem extends OdooDomRecordLike { 12 | @Attribute("name") 13 | GenericAttributeValue getNameAttribute(); 14 | 15 | @Attribute("groups") 16 | @Referencing(OdooGroupsReferenceConverter.class) 17 | GenericAttributeValue getGroupsAttribute(); 18 | 19 | @Attribute("parent") 20 | @Referencing(ParentReferenceConverter.class) 21 | GenericAttributeValue getParentIdAttribute(); 22 | 23 | @Attribute("action") 24 | @Referencing(ActionReferenceConverter.class) 25 | GenericAttributeValue getActionAttribute(); 26 | 27 | @Override 28 | default String getModel() { 29 | return OdooNames.IR_UI_MENU; 30 | } 31 | 32 | class ParentReferenceConverter implements CustomReferenceConverter { 33 | @Override 34 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 35 | PsiElement element, 36 | ConvertContext context) { 37 | return new PsiReference[]{ 38 | new OdooExternalIdReference(element, null, OdooRecordFilters.IR_UI_MENU, true) 39 | }; 40 | } 41 | } 42 | 43 | class ActionReferenceConverter implements CustomReferenceConverter { 44 | @Override 45 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 46 | PsiElement element, 47 | ConvertContext context) { 48 | return new PsiReference[]{ 49 | new OdooExternalIdReference(element, null, OdooRecordFilters.ACTION_MODELS, true) 50 | }; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomModelScoped.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | public interface OdooDomModelScoped { 4 | default String getModel() { 5 | return null; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomOperationContainer.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | public interface OdooDomOperationContainer extends OdooDomElement { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomRecord.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.*; 4 | import dev.ngocta.pycharm.odoo.OdooNames; 5 | import dev.ngocta.pycharm.odoo.data.OdooRecordExtraInfo; 6 | import dev.ngocta.pycharm.odoo.data.OdooRecordViewInfo; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.List; 11 | 12 | public interface OdooDomRecord extends OdooDomRecordLike { 13 | @Attribute("model") 14 | @Required 15 | @Referencing(OdooModelReferenceConverter.class) 16 | GenericAttributeValue getModelAttribute(); 17 | 18 | @SubTag("field") 19 | List getFields(); 20 | 21 | @Nullable 22 | default OdooDomFieldAssignment findField(@NotNull String name) { 23 | List fields = getFields(); 24 | for (OdooDomFieldAssignment field : fields) { 25 | if (name.equals(field.getName())) { 26 | return field; 27 | } 28 | } 29 | return null; 30 | } 31 | 32 | @Override 33 | @Nullable 34 | default String getModel() { 35 | return getModelAttribute().getStringValue(); 36 | } 37 | 38 | @Override 39 | @Nullable 40 | default OdooRecordExtraInfo getRecordExtraInfo() { 41 | if (OdooNames.IR_UI_VIEW.equals(getModel())) { 42 | OdooDomFieldAssignment modelField = findField("model"); 43 | String viewModel = modelField != null ? modelField.getStringValue() : null; 44 | OdooDomFieldAssignment inheritField = findField("inherit_id"); 45 | String inheritId = inheritField != null ? inheritField.getRefAttr().getStringValue() : null; 46 | return new OdooRecordViewInfo(null, viewModel, inheritId); 47 | } 48 | return null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomRecordLike.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.openapi.util.text.StringUtil; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.util.PlatformIcons; 6 | import com.intellij.util.xml.*; 7 | import dev.ngocta.pycharm.odoo.data.OdooRecord; 8 | import dev.ngocta.pycharm.odoo.data.OdooRecordExtraInfo; 9 | import dev.ngocta.pycharm.odoo.python.module.OdooModule; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import javax.swing.*; 14 | 15 | public interface OdooDomRecordLike extends OdooDomElement { 16 | @Required 17 | @NameValue 18 | @Attribute("id") 19 | @Referencing(OdooRecordReferenceConverter.class) 20 | GenericAttributeValue getIdAttribute(); 21 | 22 | @Nullable 23 | default String getId() { 24 | return getIdAttribute().getValue(); 25 | } 26 | 27 | @Nullable 28 | default String getModel() { 29 | return null; 30 | } 31 | 32 | @Nullable 33 | default OdooRecordExtraInfo getRecordExtraInfo() { 34 | return null; 35 | } 36 | 37 | @Nullable 38 | default OdooRecord getRecord() { 39 | if (getId() == null) { 40 | return null; 41 | } 42 | OdooModule module = getOdooModule(); 43 | if (module != null) { 44 | VirtualFile file = getFile().getVirtualFile(); 45 | return new OdooRecord(getId(), StringUtil.notNullize(getModel()), module.getName(), getRecordExtraInfo(), file); 46 | } 47 | return null; 48 | } 49 | 50 | @Override 51 | @NotNull 52 | default ElementPresentation getPresentation() { 53 | return new ElementPresentation() { 54 | @Override 55 | public String getElementName() { 56 | return getId(); 57 | } 58 | 59 | @Override 60 | public String getTypeName() { 61 | return getXmlElementName(); 62 | } 63 | 64 | @Override 65 | @Nullable 66 | public Icon getIcon() { 67 | return PlatformIcons.XML_TAG_ICON; 68 | } 69 | }; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomReport.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import dev.ngocta.pycharm.odoo.OdooNames; 4 | 5 | public interface OdooDomReport extends OdooDomRecordLike { 6 | @Override 7 | default String getModel() { 8 | return OdooNames.IR_ACTIONS_REPORT; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomTemplate.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.*; 6 | import dev.ngocta.pycharm.odoo.OdooNames; 7 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReference; 8 | import dev.ngocta.pycharm.odoo.data.OdooRecordExtraInfo; 9 | import dev.ngocta.pycharm.odoo.data.OdooRecordViewInfo; 10 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilters; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | public interface OdooDomTemplate extends OdooDomRecordLike { 15 | @Attribute("inherit_id") 16 | @Referencing(TemplateIdReferenceConverter.class) 17 | GenericAttributeValue getInheritIdAttribute(); 18 | 19 | @Override 20 | default String getModel() { 21 | return OdooNames.IR_UI_VIEW; 22 | } 23 | 24 | default String getInheritId() { 25 | return getInheritIdAttribute().getStringValue(); 26 | } 27 | 28 | default String getViewType() { 29 | return OdooNames.VIEW_TYPE_QWEB; 30 | } 31 | 32 | @Override 33 | @Nullable 34 | default OdooRecordExtraInfo getRecordExtraInfo() { 35 | return new OdooRecordViewInfo(getViewType(), null, getInheritId()); 36 | } 37 | 38 | class TemplateIdReferenceConverter implements CustomReferenceConverter { 39 | @Override 40 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 41 | PsiElement element, 42 | ConvertContext context) { 43 | return new PsiReference[]{ 44 | new OdooExternalIdReference(element, null, OdooRecordFilters.QWEB, true) 45 | }; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomViewButton.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.openapi.util.TextRange; 4 | import com.intellij.psi.ElementManipulators; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiReference; 7 | import com.intellij.util.xml.*; 8 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReference; 9 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilters; 10 | import dev.ngocta.pycharm.odoo.python.model.OdooModelFunctionPublicReference; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | public interface OdooDomViewButton extends OdooDomModelScopedViewElement { 17 | @Attribute("name") 18 | @Referencing(ButtonNameReferencing.class) 19 | GenericAttributeValue getNameAttribute(); 20 | 21 | @Attribute("type") 22 | GenericAttributeValue getTypeAttribute(); 23 | 24 | class ButtonNameReferencing implements CustomReferenceConverter { 25 | @Override 26 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 27 | PsiElement element, 28 | ConvertContext context) { 29 | String name = value.getStringValue(); 30 | OdooDomViewButton button = value.getParentOfType(OdooDomViewButton.class, true); 31 | if (name != null && button != null) { 32 | String type = button.getTypeAttribute().getStringValue(); 33 | if ("object".equals(type) || (type == null && !name.contains("%"))) { 34 | String model = button.getModel(); 35 | if (model != null) { 36 | return new PsiReference[]{new OdooModelFunctionPublicReference(element, model)}; 37 | } 38 | } else if ("action".equals(type) || (type == null && name.contains("%"))) { 39 | Matcher matcher = Pattern.compile("(?<=%\\()(\\w+\\.)?\\w+").matcher(name); 40 | if (matcher.find()) { 41 | TextRange range = ElementManipulators.getValueTextRange(element); 42 | range = range.cutOut(new TextRange(matcher.start(), matcher.end())); 43 | return new PsiReference[]{ 44 | new OdooExternalIdReference(element, range, OdooRecordFilters.ACTION_MODELS, true) 45 | }; 46 | } 47 | } 48 | } 49 | return new PsiReference[0]; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomViewElement.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.Attribute; 4 | import com.intellij.util.xml.DomElement; 5 | import com.intellij.util.xml.GenericAttributeValue; 6 | import com.intellij.util.xml.Referencing; 7 | import dev.ngocta.pycharm.odoo.OdooNames; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public interface OdooDomViewElement extends OdooDomElement { 11 | @Attribute("groups") 12 | @Referencing(OdooGroupsQualifiedReferenceConverter.class) 13 | GenericAttributeValue getGroupsAttribute(); 14 | 15 | @Nullable 16 | default String getViewType() { 17 | DomElement parent = getParent(); 18 | if (parent instanceof OdooDomTemplate) { 19 | return OdooNames.VIEW_TYPE_QWEB; 20 | } 21 | if (parent instanceof OdooDomField) { 22 | String tagName = getXmlElementName(); 23 | if ("t".equals(tagName) && parent instanceof OdooDomFieldAssignment) { 24 | return OdooNames.VIEW_TYPE_QWEB; 25 | } 26 | return tagName; 27 | } 28 | if (parent instanceof OdooDomViewElement) { 29 | return ((OdooDomViewElement) parent).getViewType(); 30 | } 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomViewField.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | public interface OdooDomViewField extends OdooDomField, OdooDomModelScopedViewElement { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomViewFieldInheritLocator.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.Attribute; 4 | import com.intellij.util.xml.GenericAttributeValue; 5 | import com.intellij.util.xml.Referencing; 6 | 7 | public interface OdooDomViewFieldInheritLocator extends OdooDomViewInheritLocator { 8 | @Attribute("name") 9 | @Referencing(OdooFieldNameReferenceConverter.class) 10 | GenericAttributeValue getNameAttr(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomViewLabel.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.Attribute; 4 | import com.intellij.util.xml.GenericAttributeValue; 5 | import com.intellij.util.xml.Referencing; 6 | 7 | public interface OdooDomViewLabel extends OdooDomModelScopedViewElement { 8 | @Attribute("for") 9 | @Referencing(OdooFieldNameReferenceConverter.class) 10 | GenericAttributeValue getForAttribute(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooDomViewXPath.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.util.xml.Attribute; 4 | import com.intellij.util.xml.GenericAttributeValue; 5 | import com.intellij.util.xml.Required; 6 | 7 | public interface OdooDomViewXPath extends OdooDomViewInheritLocator { 8 | @Attribute("expr") 9 | @Required 10 | GenericAttributeValue getExprAttribute(); 11 | 12 | default String getExpr() { 13 | return getExprAttribute().getStringValue(); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooFieldFileValueReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; 6 | import com.intellij.util.xml.ConvertContext; 7 | import com.intellij.util.xml.CustomReferenceConverter; 8 | import com.intellij.util.xml.GenericDomValue; 9 | import dev.ngocta.pycharm.odoo.OdooFileReferenceSet; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class OdooFieldFileValueReferenceConverter implements CustomReferenceConverter { 13 | @Override 14 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 15 | PsiElement element, 16 | ConvertContext context) { 17 | FileReferenceSet referenceSet = new OdooFileReferenceSet(element, true); 18 | return referenceSet.getAllReferences(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooFieldNameReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.ConvertContext; 6 | import com.intellij.util.xml.CustomReferenceConverter; 7 | import com.intellij.util.xml.DomElement; 8 | import com.intellij.util.xml.GenericDomValue; 9 | import dev.ngocta.pycharm.odoo.python.model.OdooFieldReference; 10 | import dev.ngocta.pycharm.odoo.python.model.OdooModelClass; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class OdooFieldNameReferenceConverter implements CustomReferenceConverter { 14 | @Override 15 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 16 | PsiElement element, 17 | ConvertContext context) { 18 | DomElement parent = value.getParent(); 19 | OdooModelClass modelClass = null; 20 | if (parent instanceof OdooDomModelScoped) { 21 | String model = ((OdooDomModelScoped) parent).getModel(); 22 | if (model != null && !model.isEmpty()) { 23 | modelClass = OdooModelClass.getInstance(model, element.getProject()); 24 | } 25 | } 26 | return new PsiReference[]{new OdooFieldReference(element, modelClass)}; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooFieldRefValueReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.ObjectUtils; 6 | import com.intellij.util.xml.ConvertContext; 7 | import com.intellij.util.xml.CustomReferenceConverter; 8 | import com.intellij.util.xml.DomElement; 9 | import com.intellij.util.xml.GenericDomValue; 10 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReference; 11 | import dev.ngocta.pycharm.odoo.data.OdooRecord; 12 | import dev.ngocta.pycharm.odoo.data.OdooRecordViewInfo; 13 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilter; 14 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordModelFilter; 15 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordViewModelFilter; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | public class OdooFieldRefValueReferenceConverter implements CustomReferenceConverter { 19 | @Override 20 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 21 | PsiElement element, 22 | ConvertContext context) { 23 | OdooRecordFilter filter = null; 24 | DomElement parent = value.getParent(); 25 | if (parent instanceof OdooDomFieldAssignment) { 26 | String model = ((OdooDomFieldAssignment) parent).getComodel(); 27 | OdooDomFieldAssignment field = (OdooDomFieldAssignment) parent; 28 | if ("inherit_id".equals(field.getName())) { 29 | parent = parent.getParent(); 30 | if (parent instanceof OdooDomRecord) { 31 | OdooRecord record = ((OdooDomRecord) parent).getRecord(); 32 | if (record != null) { 33 | OdooRecordViewInfo viewInfo = ObjectUtils.tryCast(record.getExtraInfo(), OdooRecordViewInfo.class); 34 | if (viewInfo != null && viewInfo.getViewModel() != null) { 35 | filter = new OdooRecordViewModelFilter(viewInfo.getViewModel(), record.getQualifiedId()); 36 | } 37 | } 38 | } 39 | } else { 40 | filter = new OdooRecordModelFilter(model); 41 | } 42 | } 43 | return new PsiReference[]{ 44 | new OdooExternalIdReference(element, null, filter, true) 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooFieldValueReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiReference; 6 | import com.intellij.util.xml.ConvertContext; 7 | import com.intellij.util.xml.CustomReferenceConverter; 8 | import com.intellij.util.xml.GenericDomValue; 9 | import dev.ngocta.pycharm.odoo.OdooNames; 10 | import dev.ngocta.pycharm.odoo.python.model.OdooModelReference; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class OdooFieldValueReferenceConverter implements CustomReferenceConverter { 14 | private static final ImmutableMap knownModelNameFields = ImmutableMap.builder() 15 | .put("model", OdooNames.IR_UI_VIEW) 16 | .put("res_model", OdooNames.IR_ACTIONS_ACT_WINDOW) 17 | .build(); 18 | 19 | @Override 20 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 21 | PsiElement element, 22 | ConvertContext context) { 23 | if (value instanceof OdooDomFieldAssignment) { 24 | String fieldName = ((OdooDomFieldAssignment) value).getNameAttr().getStringValue(); 25 | String model = knownModelNameFields.get(fieldName); 26 | if (model != null && model.equals(((OdooDomFieldAssignment) value).getModel())) { 27 | return new PsiReference[]{new OdooModelReference(element)}; 28 | } 29 | } 30 | return new PsiReference[0]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooGroupsQualifiedReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.ConvertContext; 6 | import com.intellij.util.xml.CustomReferenceConverter; 7 | import com.intellij.util.xml.GenericDomValue; 8 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReferenceProvider; 9 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilters; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class OdooGroupsQualifiedReferenceConverter implements CustomReferenceConverter { 13 | @Override 14 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 15 | PsiElement element, 16 | ConvertContext context) { 17 | return OdooExternalIdReferenceProvider.getCommaSeparatedReferences(element, OdooRecordFilters.RES_GROUPS, false); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooGroupsReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.ConvertContext; 6 | import com.intellij.util.xml.CustomReferenceConverter; 7 | import com.intellij.util.xml.GenericDomValue; 8 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReferenceProvider; 9 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilters; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class OdooGroupsReferenceConverter implements CustomReferenceConverter { 13 | @Override 14 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 15 | PsiElement element, 16 | ConvertContext context) { 17 | return OdooExternalIdReferenceProvider.getCommaSeparatedReferences(element, OdooRecordFilters.RES_GROUPS, true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooModelReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.util.xml.ConvertContext; 6 | import com.intellij.util.xml.CustomReferenceConverter; 7 | import com.intellij.util.xml.GenericDomValue; 8 | import dev.ngocta.pycharm.odoo.python.model.OdooModelReference; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class OdooModelReferenceConverter implements CustomReferenceConverter { 12 | @Override 13 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 14 | PsiElement element, 15 | ConvertContext context) { 16 | return new PsiReference[]{new OdooModelReference(element)}; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/dom/OdooRecordReferenceConverter.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.dom; 2 | 3 | import com.intellij.openapi.util.text.StringUtil; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiReference; 6 | import com.intellij.util.xml.ConvertContext; 7 | import com.intellij.util.xml.CustomReferenceConverter; 8 | import com.intellij.util.xml.DomUtil; 9 | import com.intellij.util.xml.GenericDomValue; 10 | import dev.ngocta.pycharm.odoo.data.OdooExternalIdReference; 11 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordFilter; 12 | import dev.ngocta.pycharm.odoo.data.filter.OdooRecordModelFilter; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | public class OdooRecordReferenceConverter implements CustomReferenceConverter { 16 | @Override 17 | public PsiReference @NotNull [] createReferences(GenericDomValue value, 18 | PsiElement element, 19 | ConvertContext context) { 20 | OdooDomRecordLike domRecordLike = DomUtil.findDomElement(element, OdooDomRecordLike.class); 21 | OdooRecordFilter filter = null; 22 | if (domRecordLike != null && !StringUtil.isEmpty(domRecordLike.getModel())) { 23 | filter = new OdooRecordModelFilter(domRecordLike.getModel()); 24 | } 25 | return new PsiReference[]{new OdooExternalIdReference(element, null, filter, true)}; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/xpath/OdooXPathFunctionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.xpath; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import com.intellij.psi.xml.XmlTag; 5 | import org.intellij.lang.xpath.context.ContextType; 6 | import org.intellij.lang.xpath.context.functions.Function; 7 | import org.intellij.lang.xpath.context.functions.Parameter; 8 | import org.intellij.lang.xpath.context.functions.XPathFunctionProvider; 9 | import org.intellij.lang.xpath.psi.XPathType; 10 | import org.intellij.plugins.xpathView.support.jaxen.extensions.FunctionImplementation; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import javax.xml.namespace.QName; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class OdooXPathFunctionProvider extends XPathFunctionProvider { 19 | @NotNull 20 | @Override 21 | public Map getFunctions(ContextType contextType) { 22 | return ImmutableMap.builder() 23 | .put(new QName(XPathFunctionHasClass.NAME), new XPathFunctionHasClass()) 24 | .build(); 25 | } 26 | 27 | static class XPathFunctionHasClass extends FunctionImplementation { 28 | public static final String NAME = "hasclass"; 29 | 30 | public XPathFunctionHasClass() { 31 | super(NAME, XPathType.BOOLEAN, new Parameter(XPathType.STRING, Parameter.Kind.VARARG)); 32 | } 33 | 34 | @Override 35 | public org.jaxen.Function getImplementation() { 36 | return (context, list) -> { 37 | if (list.isEmpty()) { 38 | return Boolean.FALSE; 39 | } 40 | for (Object item : context.getNodeSet()) { 41 | if (item instanceof XmlTag) { 42 | String classAttr = ((XmlTag) item).getAttributeValue("class"); 43 | if (classAttr == null) { 44 | return Boolean.FALSE; 45 | } 46 | List classes = Arrays.asList(classAttr.split("\\s+")); 47 | for (Object cls : list) { 48 | if (!classes.contains(cls)) { 49 | return Boolean.FALSE; 50 | } 51 | } 52 | return Boolean.TRUE; 53 | } 54 | } 55 | return Boolean.FALSE; 56 | }; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/dev/ngocta/pycharm/odoo/xml/xpath/OdooXPathLanguageInjector.java: -------------------------------------------------------------------------------- 1 | package dev.ngocta.pycharm.odoo.xml.xpath; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.patterns.XmlAttributeValuePattern; 6 | import com.intellij.patterns.XmlPatterns; 7 | import com.intellij.psi.ElementManipulators; 8 | import com.intellij.psi.InjectedLanguagePlaces; 9 | import com.intellij.psi.LanguageInjector; 10 | import com.intellij.psi.PsiLanguageInjectionHost; 11 | import dev.ngocta.pycharm.odoo.xml.OdooXmlUtils; 12 | import org.intellij.lang.xpath.XPathLanguage; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | public class OdooXPathLanguageInjector implements LanguageInjector { 16 | public static final Language XPATH_LANG = Language.findInstance(XPathLanguage.class); 17 | public static final XmlAttributeValuePattern XPATH_PATTERN = 18 | XmlPatterns.xmlAttributeValue().withParent( 19 | XmlPatterns.xmlAttribute("expr").withParent( 20 | XmlPatterns.xmlTag().withLocalName("xpath").with( 21 | OdooXmlUtils.ODOO_XML_ELEMENT_PATTERN_CONDITION))); 22 | 23 | @Override 24 | public void getLanguagesToInject(@NotNull PsiLanguageInjectionHost host, 25 | @NotNull InjectedLanguagePlaces injectionPlacesRegistrar) { 26 | if (XPATH_LANG != null && XPATH_PATTERN.accepts(host)) { 27 | TextRange textRange = ElementManipulators.getValueTextRange(host); 28 | injectionPlacesRegistrar.addPlace(XPATH_LANG, textRange, null, null); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin-css.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin-csv.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin-javascript.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin-xpath.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/inspectionDescriptions/OdooCsvUnresolvedReferences.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 |

5 | 6 |

7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/inspectionDescriptions/OdooPyTranslationStringFormat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 |

5 | 6 |

7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/inspectionDescriptions/OdooXml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 |

5 | 6 |

7 | 8 | -------------------------------------------------------------------------------- /src/main/resources/liveTemplates/Python.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | --------------------------------------------------------------------------------