├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── gradle.yml ├── .gitignore ├── .sentryclirc ├── CHANGELOG.md ├── DESCRIPTION.md ├── LICENSE.md ├── README.md ├── Signature_implementation.patch ├── TODO.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── logo.png ├── s1.png ├── s2.png └── s3.png ├── resources ├── META-INF │ ├── phpstorm-remote-interpreter.xml │ ├── plugin.xml │ ├── pluginIcon.svg │ ├── terminal.xml │ └── twig.xml ├── fileTemplates │ ├── Yii2 PHP View File.php.ft │ ├── Yii2 Smarty View File.tpl.ft │ └── Yii2 Twig View File.twig.ft ├── icons │ ├── migration_tool_window.png │ └── migration_tool_window@2x.png └── inspectionDescriptions │ ├── MissedFieldInspection.html │ ├── MissedParamInspection.html │ ├── MissedViewInspection.html │ ├── MissingActiveRecordInActiveQueryInspection.html │ ├── PropertiesInspection.html │ ├── RequireParameterInspection.html │ ├── UndetectableTableInspection.html │ ├── UnusedParameterInspection.html │ └── ViewMissedPhpDocInspection.html ├── settings.gradle ├── src └── com │ └── nvlad │ └── yii2support │ ├── PluginApplicationComponent.java │ ├── PluginGlobalSettings.java │ ├── Yii2SupportIcons.java │ ├── attributeLabels │ ├── AttributeLabelCompletionContributor.java │ └── AttributeLabelCompletionProvider.java │ ├── common │ ├── ClassUtils.java │ ├── DatabaseUtils.java │ ├── FileUtil.java │ ├── MethodUtils.java │ ├── Patterns.java │ ├── PhpUtil.java │ ├── PsiUtil.java │ ├── SignatureUtils.java │ ├── StringUtils.java │ ├── UrlUtils.java │ ├── VirtualProperty.java │ ├── YiiAlias.java │ ├── YiiApplicationTemplate.java │ ├── YiiApplicationUtils.java │ ├── YiiCommandLineUtil.java │ ├── YiiIndexKeys.java │ └── YiiModuleUtils.java │ ├── configurations │ ├── ComponentsIndex.java │ ├── YiiAppCompletionContributor.java │ └── YiiAppCompletionProvider.java │ ├── database │ ├── KNOWNBUGS.md │ ├── MissedParamInspection.java │ ├── MissedParamQuickFix.java │ ├── MissingActiveRecordInActiveQueryInspection.java │ ├── ParamsCompletionContributor.java │ ├── ParamsCompletionProvider.java │ ├── QueryCompletionContributor.java │ ├── QueryCompletionProvider.java │ ├── README.md │ ├── TableInfo.java │ ├── UndetectableTableInspection.java │ └── settings │ │ ├── SettingsForm.form │ │ └── SettingsForm.java │ ├── errorreport │ ├── ErrorReportHandler.java │ └── SentryErrorReporter.java │ ├── forms │ ├── ActiveFormCompletionContributor.java │ ├── FieldAttributesCompletionContributor.java │ └── FieldAttributesCompletionProvider.java │ ├── i18n │ ├── CategoryLookupElement.java │ ├── CompletionContributor.java │ ├── CompletionProvider.java │ ├── MessageLookupElement.java │ └── Util.java │ ├── migrations │ ├── actions │ │ ├── MigrateBaseAction.java │ │ ├── MigrateDownAction.java │ │ ├── MigrateRedoAction.java │ │ ├── MigrateUpAction.java │ │ ├── OrderAscAction.java │ │ └── RefreshAction.java │ ├── commands │ │ ├── CommandBase.java │ │ ├── CommandUpDownRedoBase.java │ │ ├── MigrationDown.java │ │ ├── MigrationHistory.java │ │ ├── MigrationRedo.java │ │ └── MigrationUp.java │ ├── entities │ │ ├── DefaultMigrateCommand.java │ │ ├── MigrateCommand.java │ │ ├── MigrateCommandComparator.java │ │ ├── Migration.java │ │ ├── MigrationComparator.java │ │ └── MigrationStatus.java │ ├── services │ │ ├── MigrationService.java │ │ ├── MigrationServiceListener.java │ │ └── MigrationsVirtualFileMonitor.java │ ├── ui │ │ ├── settings │ │ │ ├── ComboBoxTableCellEditor.java │ │ │ ├── MigrateCommandTableModel.java │ │ │ ├── MigrationCommandDialog.java │ │ │ ├── MigrationCommandDialogValidator.java │ │ │ ├── MigrationPanel.java │ │ │ ├── StringListEditPanel.java │ │ │ ├── StringListTableModel.java │ │ │ └── entities │ │ │ │ └── TableModelStringEntity.java │ │ └── toolWindow │ │ │ ├── ConsolePanel.java │ │ │ ├── MigrationPanel.java │ │ │ ├── MigrationTreeCellRenderer.java │ │ │ ├── MigrationsMouseListener.java │ │ │ └── MigrationsToolWindowFactory.java │ └── util │ │ ├── MigrationUtil.java │ │ └── TreeUtil.java │ ├── objectfactory │ ├── ObjectFactoryCompletionContributor.java │ ├── ObjectFactoryCompletionProvider.java │ ├── ObjectFactoryMissedFieldInspection.java │ ├── ObjectFactoryReference.java │ ├── ObjectFactoryReferenceContributor.java │ ├── ObjectFactoryReferenceProvider.java │ ├── ObjectFactoryUtils.java │ └── README.md │ ├── properties │ ├── MissingPropertiesQuickFix.java │ ├── PropertiesInspection.java │ └── TODO.md │ ├── typeprovider │ ├── ActiveRecordTypeProvider.java │ └── YiiTypeProvider.java │ ├── ui │ └── settings │ │ ├── SettingsForm.form │ │ └── SettingsForm.java │ ├── url │ └── UrlCompletionContributor.java │ ├── utils │ └── Yii2SupportSettings.java │ ├── validation │ ├── RulePositionEnum.java │ ├── ValidationCompletionContributor.java │ ├── ValidationCompletionProvider.java │ ├── ValidationUtil.java │ └── entities │ │ └── Validator.java │ ├── views │ ├── actions │ │ ├── OpenViewCalls.java │ │ └── ReferenceListPopupStep.java │ ├── completion │ │ ├── CompletionContributor.java │ │ ├── CompletionProvider.java │ │ └── ViewLookupElement.java │ ├── entities │ │ ├── ViewInfo.java │ │ ├── ViewResolve.java │ │ └── ViewResolveFrom.java │ ├── index │ │ └── ViewFileIndex.java │ ├── inspections │ │ ├── MissedViewInspection.java │ │ ├── MissedViewLocalQuickFix.java │ │ ├── RequireParameterInspection.java │ │ ├── RequireParameterLocalQuickFix.java │ │ ├── UnusedParameterInspection.java │ │ ├── UnusedParameterLocalQuickFix.java │ │ ├── UnusedParametersLocalQuickFix.java │ │ ├── ViewMissedPhpDocInspection.java │ │ └── ViewMissedPhpDocLocalQuickFix.java │ ├── navigation │ │ └── ViewGotoDeclarationHandler.java │ ├── refactor │ │ └── RenameViewProcessor.java │ ├── references │ │ ├── PsiReference.java │ │ ├── PsiReferenceContributor.java │ │ └── PsiReferenceProvider.java │ ├── settings │ │ ├── EditPathMapEntryPanel.java │ │ ├── EditThemePathMapDialog.java │ │ ├── ThemePathMapPanel.java │ │ ├── ThemePathMapTableModel.java │ │ ├── ViewSettings.form │ │ └── ViewSettings.java │ └── util │ │ ├── RenderUtil.java │ │ └── ViewUtil.java │ └── widgetsconfig │ ├── WidgetConfigCompletionContributor.java │ └── WidgetConfigCompletionProvider.java ├── tests └── com │ └── nvlad │ └── yii2support │ ├── common │ └── StringUtilsTest.java │ ├── database │ ├── ActiveQueryNoConnection.java │ ├── ActiveQueryTest.java │ ├── QueryTests.java │ └── fixtures │ │ ├── TestColumn.java │ │ ├── TestDataSource.java │ │ ├── TestModel.java │ │ ├── TestNamespace.java │ │ ├── TestTable.java │ │ └── classes.php │ ├── objectfactory │ ├── ObjectFactoryTests.java │ └── fixtures │ │ └── classes.php │ ├── url │ ├── UrlTests.java │ └── fixtures │ │ ├── classes.php │ │ ├── testControllerRedirectMethod.php │ │ ├── testControllerRedirectMethodArrayArgument.php │ │ ├── testUrlParameterCompletion.php │ │ ├── testUrlRemember.php │ │ ├── testUrlRememberArrayArgument.php │ │ ├── testUrlTo.php │ │ └── testUrlToArrayArgument.php │ └── validation │ ├── ValidationTests.java │ └── fixtures │ └── classes.php └── yii2support.iml /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What steps will reproduce the problem? 2 | 3 | 4 | ### What is the expected result? 5 | 6 | 7 | ### What do you get instead? 8 | 9 | 10 | ### Additional info 11 | 12 | | Q | A 13 | | ---------------- | -------- 14 | | IDE Name | PhpStorm 15 | | IDE Version | 16 | | Plugin version | 17 | | Yii App Template | basic / advanced 18 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Execute Plugin tests with Gradle 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | name: Run Unit Tests 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout Code 19 | uses: actions/checkout@v2 20 | - name: Set up JDK 21 | uses: actions/setup-java@v1 22 | with: 23 | java-version: 11 24 | - name: Grant execute permission for gradlew 25 | run: chmod +x gradlew 26 | - name: Run Tests with Gradle 27 | run: ./gradlew test 28 | - name: Publish Test Report 29 | uses: mikepenz/action-junit-report@v1 30 | with: 31 | report_paths: '**/build/test-results/test/TEST-*.xml' 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | .idea/ 3 | yii2support.jar 4 | yii2support.zip 5 | /build 6 | /.gradle/ 7 | /bin/* 8 | .* -------------------------------------------------------------------------------- /.sentryclirc: -------------------------------------------------------------------------------- 1 | [auth] 2 | token=enter token here 3 | 4 | [defaults] 5 | url=https://sentry.nvlad.com/ 6 | org=nvlad 7 | project=yii2support 8 | -------------------------------------------------------------------------------- /DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | Provides Yii2 Framework support for PhpStorm and IntelliJ IDEA 2 | ============================================================== 3 | 4 | Plugin contains the following functionality 5 | - Suggests for render() methods 6 | - I18n 7 | - Configuration arrays for object instantiation 8 | - ActiveQuery, Query and migrations (database connection is required) 9 | - Autocomplete in model's rules method 10 | - Calculates return type for Yii::createObject call and one/all method calls of ActiveRecord 11 | - Url autocomplete 12 | - Migration tool 13 | 14 | Detailed information can be found in [README.md](https://github.com/nvlad/yii2support/blob/master/README.md) 15 | Detailed changelog can be found in [CHANGELOG.md](https://github.com/nvlad/yii2support/blob/master/CHANGELOG.md) 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Yii2 Support is free software. It is released under the terms of 2 | the following BSD License. 3 | 4 | Copyright © 2017 by Vladislav Nikishin aka NVlad (wildvampir@gmail.com) 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | * Neither the name of Yii2 Support nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /Signature_implementation.patch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvlad/yii2support/47aceca6b3e952a36ab2993ab922fa8c4c50bb2b/Signature_implementation.patch -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Code completion in validation rules 2 | Code completion for urls 3 | Code completion for parameters 4 | Properties generation 5 | Phpdoc for variables in view 6 | 7 | To consider: 8 | ? Application parser for better experience 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #runIdePath = /AppData/Local/JetBrains/Toolbox/apps/PhpStorm/ch-1/193.5662.42 2 | ##runIdePath = /Users/nvlad/Library/Application Support/JetBrains/Toolbox/apps/PhpStorm/ch-2/181.5281.35/PhpStorm.app 3 | #ideVersion = 2019.1.4 4 | #phpPluginVersion = 191.8026.56 5 | #phpRemoteInterpreterPluginVersion = 191.5849.22 6 | #twigPluginVersion = 191.6183.95 7 | 8 | 9 | #runIdePath = /AppData/Local/JetBrains/Toolbox/apps/PhpStorm/ch-1/193.5662.42 10 | ##runIdePath = /Users/nvlad/Library/Application Support/JetBrains/Toolbox/apps/PhpStorm/ch-2/181.5281.35/PhpStorm.app 11 | #ideVersion = 2019.3 12 | #phpPluginVersion = 193.5233.102 13 | #phpRemoteInterpreterPluginVersion = 193.5233.57 14 | #twigPluginVersion = 193.5233.116 15 | 16 | 17 | platformType = IU 18 | #platformVersion = 2020.3.3 19 | platformVersion = 2021.2 20 | platformDownloadSources = true 21 | 22 | pluginName_ = Yii2 Support 23 | pluginVersion = 0.10.58.52 24 | pluginSinceBuild = 192 25 | #pluginVerifierIdeVersions = IU-2019.1.4, IU-2019.3.5, IU-2020.1.4, IU-2020.2.3 26 | pluginVerifierIdeVersions = IU-2020.2.3, IU-2020.3.3, IU-2021.1, IU-2021.2 27 | 28 | #runIdePath = /home/gl/.local/share/JetBrains/Toolbox/apps/PhpStorm/ch-1/211.6693.78 29 | #ideVersion = LATEST-EAP-SNAPSHOT 30 | #phpPluginVersion = 203.5251.40 31 | #phpRemoteInterpreterPluginVersion = 203.5251.40 32 | #twigPluginVersion = 203.5251.28 33 | 34 | org.gradle.jvmargs=-Xmx4608m 35 | 36 | ORG_GRADLE_PROJECT_USERNAME= 37 | ORG_GRADLE_PROJECT_PASSWORD= 38 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvlad/yii2support/47aceca6b3e952a36ab2993ab922fa8c4c50bb2b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvlad/yii2support/47aceca6b3e952a36ab2993ab922fa8c4c50bb2b/img/logo.png -------------------------------------------------------------------------------- /img/s1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvlad/yii2support/47aceca6b3e952a36ab2993ab922fa8c4c50bb2b/img/s1.png -------------------------------------------------------------------------------- /img/s2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvlad/yii2support/47aceca6b3e952a36ab2993ab922fa8c4c50bb2b/img/s2.png -------------------------------------------------------------------------------- /img/s3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvlad/yii2support/47aceca6b3e952a36ab2993ab922fa8c4c50bb2b/img/s3.png -------------------------------------------------------------------------------- /resources/META-INF/phpstorm-remote-interpreter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/META-INF/terminal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /resources/META-INF/twig.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/fileTemplates/Yii2 PHP View File.php.ft: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reports missing object properties. 4 | 5 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/MissedParamInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Analyzes SQL condition and checks if all declared parameters are set 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/MissedViewInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reports missing view templates 4 | 5 |

6 | - QuickFix - create view template 7 |

8 | 9 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/MissingActiveRecordInActiveQueryInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Check if ActiveQuery correctly linked to ActiveRecord 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/PropertiesInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Detects properties do not correspond to class fields or table columns in case of ActiveRecord 4 | 5 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/RequireParameterInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Reports missing view template parameters 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/UndetectableTableInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Finds ActiveRecord table and check if it exists in database connections 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/UnusedParameterInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Detecting unused View parameters. 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/inspectionDescriptions/ViewMissedPhpDocInspection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Inspect PhpDoc for incoming parameters. 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user guide at https://docs.gradle.org/4.9/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'yii2support' 11 | 12 | buildCache { 13 | local { 14 | // Set local build cache directory. 15 | directory = "${settingsDir}/build-cache" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/PluginApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support; 2 | 3 | import com.intellij.ide.plugins.IdeaPluginDescriptor; 4 | import com.intellij.ide.plugins.PluginManager; 5 | import com.intellij.notification.*; 6 | import com.intellij.openapi.application.ApplicationManager; 7 | import com.intellij.openapi.components.ApplicationComponent; 8 | import com.intellij.openapi.extensions.PluginId; 9 | import com.intellij.openapi.util.text.StringUtil; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.UUID; 13 | 14 | public class PluginApplicationComponent implements ApplicationComponent { 15 | public static final PluginId PLUGIN_ID = PluginId.getId("com.yii2support"); 16 | 17 | @Override 18 | public void initComponent() { 19 | final IdeaPluginDescriptor plugin = PluginManager.getPlugin(PLUGIN_ID); 20 | if (plugin == null) { 21 | return; 22 | } 23 | 24 | final PluginGlobalSettings settings = PluginGlobalSettings.getInstance(); 25 | if (StringUtil.isEmpty(settings.uuid)) { 26 | settings.uuid = UUID.randomUUID().toString(); 27 | } 28 | 29 | if (StringUtil.isEmpty(settings.username)) { 30 | settings.username = settings.uuid; 31 | } 32 | 33 | if (!plugin.getVersion().equals(settings.version)) { 34 | settings.version = plugin.getVersion(); 35 | 36 | String popupTitle = plugin.getName() + " v" + plugin.getVersion(); 37 | NotificationGroup group = new NotificationGroup(plugin.getName(), NotificationDisplayType.STICKY_BALLOON, true); 38 | Notification notification = group.createNotification( 39 | popupTitle, plugin.getChangeNotes(), NotificationType.INFORMATION, NotificationListener.URL_OPENING_LISTENER 40 | ); 41 | Notifications.Bus.notify(notification); 42 | } 43 | } 44 | 45 | @Override 46 | public void disposeComponent() { 47 | } 48 | 49 | @NotNull 50 | @Override 51 | public String getComponentName() { 52 | return "PluginApplicationComponent"; 53 | } 54 | 55 | public static PluginApplicationComponent getInstance() { 56 | return ApplicationManager.getApplication().getComponent(PluginApplicationComponent.class); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/PluginGlobalSettings.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.ServiceManager; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.components.Storage; 7 | import com.intellij.util.xmlb.XmlSerializerUtil; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | @State(name = "Yii2 Support", storages = @Storage(file = "$APP_CONFIG$/yii2support.xml")) 11 | public class PluginGlobalSettings implements PersistentStateComponent { 12 | public String version; 13 | public String uuid; 14 | public String username; 15 | 16 | @Nullable 17 | @Override 18 | public PluginGlobalSettings getState() { 19 | return this; 20 | } 21 | 22 | @Override 23 | public void loadState(PluginGlobalSettings settings) { 24 | XmlSerializerUtil.copyBean(settings, this); 25 | } 26 | 27 | public static PluginGlobalSettings getInstance() { 28 | return ServiceManager.getService(PluginGlobalSettings.class); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/Yii2SupportIcons.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | public interface Yii2SupportIcons { 8 | Icon MIGRATION_TOOL_WINDOW = IconLoader.getIcon("/icons/migration_tool_window.png"); 9 | } 10 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/attributeLabels/AttributeLabelCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.attributeLabels; 2 | 3 | import com.intellij.codeInsight.completion.CompletionType; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.psi.PsiElement; 6 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class AttributeLabelCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor{ 10 | public AttributeLabelCompletionContributor() { 11 | extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new AttributeLabelCompletionProvider()); 12 | } 13 | 14 | @Override 15 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 16 | if ((typeChar == '\'' || typeChar == '"') && position.getParent() instanceof ArrayCreationExpression) { 17 | return true; 18 | } 19 | 20 | return false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/attributeLabels/AttributeLabelCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.attributeLabels; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionProvider; 5 | import com.intellij.codeInsight.completion.CompletionResultSet; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.openapi.editor.Document; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.util.ProcessingContext; 10 | import com.jetbrains.php.lang.psi.elements.*; 11 | import com.nvlad.yii2support.common.ClassUtils; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | public class AttributeLabelCompletionProvider extends CompletionProvider { 15 | @Override 16 | protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { 17 | PsiElement position = completionParameters.getPosition(); 18 | if (position.getParent() instanceof PhpExpression) { 19 | PhpExpression phpExpression = (PhpExpression) position.getParent(); 20 | PhpClass phpClass = ClassUtils.getClassIfInMethod(position, "attributeLabels"); 21 | if (phpClass != null) { 22 | for (Field field : ClassUtils.getClassFields(phpClass)) { 23 | LookupElementBuilder lookupBuilder = buildLookup(field, phpExpression); 24 | completionResultSet.addElement(lookupBuilder); 25 | } 26 | } 27 | } 28 | } 29 | 30 | @NotNull 31 | private LookupElementBuilder buildLookup(PhpClassMember field, PhpExpression position) { 32 | String lookupString = field instanceof Method ? ClassUtils.getAsPropertyName((Method) field) : field.getName(); 33 | LookupElementBuilder builder = LookupElementBuilder.create(field, lookupString).withIcon(field.getIcon()); 34 | 35 | builder = builder.withInsertHandler((insertionContext, lookupElement) -> { 36 | Document document = insertionContext.getDocument(); 37 | int insertPosition = insertionContext.getSelectionEndOffset(); 38 | if (position.getParent().getParent() instanceof ArrayCreationExpression) { 39 | document.insertString(insertPosition + 1, " => '',"); 40 | insertPosition += 6; 41 | insertionContext.getEditor().getCaretModel().getCurrentCaret().moveToOffset(insertPosition); 42 | } 43 | }); 44 | 45 | if (field instanceof Field) { 46 | builder = builder.withTypeText(field.getType().toString()); 47 | } 48 | 49 | return builder; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.openapi.vfs.VirtualFile; 4 | import com.intellij.psi.PsiFile; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class FileUtil { 8 | 9 | @NotNull 10 | public static VirtualFile getVirtualFile(PsiFile file) { 11 | VirtualFile result = file.getVirtualFile(); 12 | if (result == null) { 13 | result = file.getOriginalFile().getVirtualFile(); 14 | } 15 | return result; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/MethodUtils.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.jetbrains.php.lang.psi.elements.MethodReference; 5 | import com.jetbrains.php.lang.psi.elements.Parameter; 6 | import com.jetbrains.php.lang.psi.elements.ParameterList; 7 | import com.jetbrains.php.lang.psi.elements.PhpExpression; 8 | import com.jetbrains.php.lang.psi.elements.impl.MethodReferenceImpl; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | /** 12 | * Created by oleg on 2017-07-23. 13 | */ 14 | public class MethodUtils { 15 | @Nullable 16 | public static PsiElement getParameter(MethodReference methodRef, int index) { 17 | if (methodRef.getParameters().length >= index) { 18 | return methodRef.getParameters()[index]; 19 | } 20 | return null; 21 | } 22 | 23 | public static int paramIndexByRef(PsiElement paramRef) { 24 | if (paramRef.getParent().getParent() instanceof MethodReference) { 25 | MethodReference ref = (MethodReference) paramRef.getParent().getParent(); 26 | PsiElement[] parameters = ref.getParameters(); 27 | for (int i = 0; i < parameters.length; i++) { 28 | PsiElement parameter = parameters[i]; 29 | if (parameter == paramRef) 30 | return i; 31 | } 32 | } 33 | return -1; 34 | } 35 | 36 | @Nullable 37 | public static PsiElement findParamRefByElement(PsiElement element) { 38 | int limit = 10; 39 | PsiElement prevElement = element; 40 | PsiElement currElement = element.getParent(); 41 | while (limit > 0) { 42 | if (currElement instanceof ParameterList) 43 | return prevElement; 44 | else { 45 | prevElement = currElement; 46 | currElement = currElement.getParent(); 47 | 48 | limit--; 49 | } 50 | } 51 | return null; 52 | } 53 | 54 | public static boolean isYiiCreateObjectMethod(PsiElement psiElement) { 55 | if (psiElement instanceof MethodReference) { 56 | MethodReference referenceMethod = (MethodReference) psiElement; 57 | if (referenceMethod.getName() != null && referenceMethod.getName().equals("createObject") 58 | && referenceMethod.getParameters().length > 0) { 59 | PhpExpression classReference = ((MethodReferenceImpl) psiElement).getClassReference(); 60 | if (classReference != null && classReference.getName() != null && classReference.getName().equals("Yii")) { 61 | return true; 62 | } 63 | } 64 | } 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/Patterns.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.patterns.InitialPatternCondition; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.util.ArrayUtil; 6 | import com.intellij.util.ProcessingContext; 7 | import com.jetbrains.php.injection.PhpElementPattern.Capture; 8 | import com.jetbrains.php.lang.psi.elements.MethodReference; 9 | import com.jetbrains.php.lang.psi.elements.PhpPsiElement; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * Created by NVlad on 12.01.2017. 14 | */ 15 | public class Patterns extends PlatformPatterns { 16 | public static Capture methodWithName(String... methodNames) { 17 | return new Capture<>(new InitialPatternCondition(MethodReference.class) { 18 | @Override 19 | public boolean accepts(@Nullable Object o, ProcessingContext context) { 20 | if (o instanceof MethodReference) { 21 | String methodReferenceName = ((MethodReference) o).getName(); 22 | return methodReferenceName != null && ArrayUtil.contains(methodReferenceName, methodNames); 23 | } 24 | return super.accepts(o, context); 25 | } 26 | }); 27 | } 28 | 29 | public static Capture withHashKey() { 30 | return new Capture<>(new InitialPatternCondition(PhpPsiElement.class) { 31 | @Override 32 | public boolean accepts(@Nullable Object o, ProcessingContext context) { 33 | return o != null && o.toString().equals("Array key"); 34 | } 35 | }); 36 | } 37 | } -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/PhpUtil.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 5 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 6 | import com.jetbrains.php.lang.psi.elements.ArrayHashElement; 7 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.Collection; 11 | import java.util.HashSet; 12 | 13 | /** 14 | * Created by NVlad on 23.01.2017. 15 | */ 16 | public class PhpUtil { 17 | @NotNull 18 | public static Collection getArrayKeys(ArrayCreationExpression array) { 19 | final HashSet result = new HashSet<>(); 20 | 21 | Iterable items = array.getHashElements(); 22 | 23 | for (ArrayHashElement item : items) { 24 | if (item.getKey() != null && item.getKey() instanceof StringLiteralExpression) { 25 | result.add(((StringLiteralExpression) item.getKey()).getContents()); 26 | } 27 | } 28 | 29 | return result; 30 | } 31 | 32 | @NotNull 33 | public static String getValue(PsiElement expression) { 34 | if (expression instanceof LeafPsiElement) { 35 | return ((LeafPsiElement) expression).getText(); 36 | } 37 | 38 | if (expression instanceof StringLiteralExpression) { 39 | String value = ((StringLiteralExpression) expression).getContents(); 40 | if (value.contains("IntellijIdeaRulezzz ")) { 41 | return value.substring(0, value.indexOf("IntellijIdeaRulezzz ")); 42 | } 43 | return value; 44 | } 45 | 46 | return ""; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/SignatureUtils.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.jetbrains.php.PhpIndex; 5 | import com.jetbrains.php.lang.psi.elements.PhpClass; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.util.Collection; 9 | 10 | /** 11 | * Created by oleg on 2017-07-22. 12 | */ 13 | public class SignatureUtils { 14 | @Nullable 15 | public static PhpClass getClassBySignature(String signature, Project project) { 16 | int beginClassIndex = signature.indexOf("\\"); 17 | int endClassIndex = signature.indexOf("."); 18 | if (endClassIndex < 0) { 19 | endClassIndex = signature.length(); 20 | } 21 | if (beginClassIndex > -1 && beginClassIndex < endClassIndex) { 22 | String className = signature.substring(beginClassIndex, endClassIndex); 23 | Collection classesByFQN = PhpIndex.getInstance(project).getClassesByFQN(className); 24 | if (! classesByFQN.isEmpty()) { 25 | return classesByFQN.iterator().next(); 26 | } 27 | } 28 | return null; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | /** 4 | * Created by oleg on 14.04.2017. 5 | */ 6 | public class StringUtils { 7 | public static String CamelToId(String name) { 8 | return CamelToId(name, "_"); 9 | } 10 | 11 | public static String CamelToId(String name, String separator) { 12 | String regex = "(?<=[a-z0-9])[A-Z]"; 13 | return name.trim().replaceAll(regex, separator + "$0").toLowerCase(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/UrlUtils.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.jetbrains.php.PhpIndex; 5 | import com.jetbrains.php.lang.psi.elements.Method; 6 | import com.jetbrains.php.lang.psi.elements.Parameter; 7 | import com.jetbrains.php.lang.psi.elements.PhpClass; 8 | 9 | import java.util.*; 10 | 11 | /** 12 | * Created by oleg on 25.04.2017. 13 | */ 14 | public class UrlUtils { 15 | private static final List excludeControllers = Arrays.asList( 16 | "\\yii\\rest\\ActiveController", 17 | "\\yii\\gii\\controllers\\DefaultController", 18 | "\\yii\\rest\\Controller", 19 | "\\yii\\debug\\controllers\\DefaultController", 20 | "\\yii\\debug\\controllers\\UserController" 21 | ); 22 | 23 | private static Collection getClassesByParent(String parentFqn, Project project) { 24 | Collection subclasses = new ArrayList<>(); 25 | Collection directSubclasses = PhpIndex.getInstance(project).getDirectSubclasses(parentFqn); 26 | for (PhpClass directSubclass: directSubclasses) { 27 | subclasses.addAll(getClassesByParent(directSubclass.getFQN(), project)); 28 | } 29 | subclasses.addAll(directSubclasses); 30 | return subclasses; 31 | } 32 | 33 | private static Collection getControllers(Project project) { 34 | return getClassesByParent("\\yii\\web\\Controller", project); 35 | } 36 | 37 | public static HashMap getRoutes(Project project) { 38 | Collection controllers = getControllers(project); 39 | HashMap routes = new HashMap<>(); 40 | for (PhpClass controller: controllers) { 41 | if (!excludeControllers.contains(controller.getFQN())) 42 | routes.putAll(controllerToRoutes(controller)); 43 | } 44 | 45 | return routes; 46 | } 47 | 48 | private static HashMap controllerToRoutes(PhpClass controller) { 49 | String controllerName = controller.getName(); 50 | String controllerPart = controllerName.substring(0, controllerName.length() - 10); 51 | controllerPart = StringUtils.CamelToId(controllerPart, "-"); 52 | 53 | Collection methods = controller.getMethods(); 54 | HashMap routes = new HashMap<>(); 55 | for (Method method: methods ) { 56 | String methodName = method.getName(); 57 | if (methodName.length() > 6 && methodName.startsWith("action") && Character.isUpperCase(methodName.charAt(6))) { 58 | String actionPart = methodName.substring(6); // remove "action" prefix 59 | actionPart = StringUtils.CamelToId(actionPart, "-"); // part2.replaceAll("(?<=[\\p{Lower}\\p{Digit}])[\\p{Upper}]", "-$0").toLowerCase(); 60 | 61 | routes.put(controllerPart + "/" +actionPart, method); 62 | } 63 | } 64 | 65 | return routes; 66 | } 67 | 68 | public static Parameter[] getParamsByUrl(String url, Project project) { 69 | final HashMap routes = getRoutes(project); 70 | if (routes.containsKey(url)) { 71 | Method method = routes.get(url); 72 | return method.getParameters(); 73 | } 74 | 75 | return null; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/VirtualProperty.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * Created by oleg on 06.04.2017. 7 | */ 8 | public class VirtualProperty { 9 | private String name; 10 | private String type; 11 | private String comment; 12 | private String prevColumn; 13 | 14 | public String getName() { 15 | return name; 16 | } 17 | 18 | public void setName(String name) { 19 | this.name = name; 20 | } 21 | 22 | public String getType() { 23 | return type; 24 | } 25 | 26 | public void setType(String type) { 27 | this.type = type; 28 | } 29 | 30 | public String getComment() { 31 | return comment; 32 | } 33 | 34 | public void setComment(String comment) { 35 | this.comment = comment; 36 | } 37 | 38 | public String getPrevColumn() { 39 | return prevColumn; 40 | } 41 | 42 | public void setPrevColumn(String prevColumn) { 43 | this.prevColumn = prevColumn; 44 | } 45 | 46 | public VirtualProperty(String name, String typeName, String typeFull, String comment, String prevColumn) { 47 | this.name = name; 48 | this.type = DbTypeToSql(typeName); 49 | 50 | this.comment = comment != null ? comment : ""; 51 | if (! typeName.contains("text")) { 52 | this.comment = "[" + typeFull + "] " + this.comment; 53 | } 54 | 55 | this.prevColumn = prevColumn; 56 | } 57 | 58 | @NotNull 59 | private String DbTypeToSql(String type) { 60 | switch (type.toUpperCase()) { 61 | case "INT": return "int"; 62 | case "TINYINT": return "bool"; 63 | case "BOOLEAN": return "bool"; 64 | case "BIGINT": return "int"; 65 | case "SMALLINT": return "int"; 66 | case "NUMERIC": return "int"; 67 | case "MEDIUMINT": return "int"; 68 | case "SMALLSERIAL": return "int"; 69 | case "SERIAL": return "int"; 70 | case "BIGSERIAL": return "int"; 71 | case "FLOAT": return "float"; 72 | case "REAL": return "float"; 73 | case "DOUBLE PRECISION": return "float"; 74 | case "DOUBLE": return "float"; 75 | case "TIMESTAMP": return "int"; 76 | } 77 | return "string"; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/YiiAlias.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 6 | import org.apache.commons.lang.StringUtils; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.WeakHashMap; 13 | 14 | public class YiiAlias { 15 | private static Map yiiProjectAliasMap = new HashMap<>(); 16 | 17 | public static YiiAlias getInstance(Project project) { 18 | if (!yiiProjectAliasMap.containsKey(project)) { 19 | yiiProjectAliasMap.put(project, new YiiAlias(project)); 20 | } 21 | 22 | return yiiProjectAliasMap.get(project); 23 | } 24 | 25 | private final Project myProject; 26 | private final Map myAliasMap; 27 | private final Map myResolvedAliasCache; 28 | 29 | protected YiiAlias(Project project) { 30 | myProject = project; 31 | myAliasMap = new HashMap<>(new WeakHashMap<>(Yii2SupportSettings.getInstance(project).aliasMap)); 32 | myResolvedAliasCache = new HashMap<>(); 33 | } 34 | 35 | @Nullable 36 | public String getAlias(@NotNull String alias, boolean console) { 37 | return aliasFromMap(myAliasMap, alias, console); 38 | } 39 | 40 | @Nullable 41 | public String resolveAlias(@NotNull String alias, boolean console) { 42 | String path = myResolvedAliasCache.get(alias); 43 | if (path != null) { 44 | return path; 45 | } 46 | 47 | path = getAlias(alias, console); 48 | if (path == null) { 49 | return null; 50 | } 51 | 52 | myResolvedAliasCache.put(alias, StringUtils.stripStart(path, "/")); 53 | 54 | return myResolvedAliasCache.get(alias); 55 | } 56 | 57 | public VirtualFile resolveVirtualFile(@NotNull String alias, boolean console) { 58 | String path = resolveAlias(alias, console); 59 | if (path == null) { 60 | return null; 61 | } 62 | 63 | path = YiiApplicationUtils.getYiiRootPath(myProject) + "/" + path; 64 | 65 | return myProject.getBaseDir().getFileSystem().findFileByPath(path); 66 | } 67 | 68 | @Nullable 69 | private String aliasFromMap(@NotNull Map aliasMap, @NotNull String alias, boolean console) { 70 | if (!alias.startsWith("@")) { 71 | return alias; 72 | } 73 | 74 | if (console && alias.startsWith("@app/")) { 75 | alias = "@yii2support-console-command-app-root" + alias.substring(4); 76 | } 77 | 78 | String value = aliasMap.get(alias); 79 | if (value != null) { 80 | return aliasFromMap(aliasMap, alias, console); 81 | } 82 | 83 | String foundAlias = null; 84 | for (String aliasKey : aliasMap.keySet()) { 85 | if (alias.startsWith(aliasKey)) { 86 | if (foundAlias == null || aliasKey.length() > foundAlias.length()) { 87 | foundAlias = aliasKey; 88 | } 89 | } 90 | } 91 | 92 | if (foundAlias == null) { 93 | return null; 94 | } 95 | 96 | value = alias.replace(foundAlias, aliasMap.get(foundAlias)); 97 | return aliasFromMap(aliasMap, value, console); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/YiiApplicationTemplate.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | public enum YiiApplicationTemplate { 4 | Basic, 5 | Advanced, 6 | StarterKit, 7 | Unknown, 8 | } 9 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/YiiIndexKeys.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.intellij.psi.stubs.StubIndexKey; 5 | 6 | public interface YiiIndexKeys { 7 | StubIndexKey VIEW = StubIndexKey.createIndexKey("yii2support.view.index"); 8 | } 9 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/common/YiiModuleUtils.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import com.intellij.openapi.vfs.VirtualFile; 4 | import com.intellij.psi.PsiFile; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public class YiiModuleUtils { 9 | @Nullable 10 | public static String getModuleName(@NotNull PsiFile file) { 11 | VirtualFile virtualFile = file.getVirtualFile(); 12 | if (virtualFile == null) { 13 | virtualFile = file.getOriginalFile().getVirtualFile(); 14 | } 15 | return getModuleName(virtualFile); 16 | } 17 | 18 | @Nullable 19 | public static String getModuleName(@NotNull VirtualFile file) { 20 | String path = getModuleUrl(file); 21 | if (path == null) { 22 | return null; 23 | } 24 | return path.substring(path.lastIndexOf('/') + 1); 25 | } 26 | 27 | @Nullable 28 | public static String getModuleUrl(@NotNull PsiFile file) { 29 | VirtualFile virtualFile = file.getVirtualFile(); 30 | if (virtualFile == null) { 31 | virtualFile = file.getOriginalFile().getVirtualFile(); 32 | } 33 | return getModuleUrl(virtualFile); 34 | } 35 | 36 | @Nullable 37 | public static String getModuleUrl(@NotNull VirtualFile file) { 38 | String path = file.getUrl(); 39 | int modulesPosition = path.lastIndexOf("/modules/"); 40 | if (modulesPosition == -1) { 41 | return null; 42 | } 43 | int pathEnd = path.indexOf('/', modulesPosition + 9); 44 | if (pathEnd != -1) { 45 | return path.substring(0, pathEnd); 46 | } 47 | return null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/configurations/YiiAppCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.configurations; 2 | 3 | import com.intellij.codeInsight.completion.CompletionContributor; 4 | import com.intellij.codeInsight.completion.CompletionType; 5 | import com.intellij.patterns.PlatformPatterns; 6 | import com.jetbrains.php.lang.psi.elements.FieldReference; 7 | 8 | public class YiiAppCompletionContributor extends CompletionContributor { 9 | public YiiAppCompletionContributor() { 10 | extend(CompletionType.BASIC, PlatformPatterns.psiElement().withParent(FieldReference.class), new YiiAppCompletionProvider()); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/configurations/YiiAppCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.configurations; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionProvider; 5 | import com.intellij.codeInsight.completion.CompletionResultSet; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.icons.AllIcons; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.psi.PsiElement; 10 | import com.intellij.psi.search.GlobalSearchScope; 11 | import com.intellij.util.ProcessingContext; 12 | import com.intellij.util.indexing.FileBasedIndex; 13 | import com.jetbrains.php.lang.psi.elements.*; 14 | import com.nvlad.yii2support.common.PsiUtil; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.List; 18 | 19 | public class YiiAppCompletionProvider extends CompletionProvider { 20 | @Override 21 | protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { 22 | final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance(); 23 | final PsiElement psiElement = completionParameters.getPosition(); 24 | final Project project = psiElement.getProject(); 25 | final GlobalSearchScope scope = GlobalSearchScope.projectScope(project); 26 | 27 | if(psiElement.getParent() instanceof FieldReference){ 28 | String field = PsiUtil.getYiiAppField((FieldReference) psiElement.getParent()); 29 | if(field != null){ 30 | fileBasedIndex.processAllKeys(ComponentsIndex.identity, key -> { 31 | List values = fileBasedIndex.getValues(ComponentsIndex.identity, key, scope); 32 | if(values.size() > 0){ 33 | LookupElementBuilder lookupElement = LookupElementBuilder.create(key) 34 | .withTypeText(values.get(0), true) 35 | .withIcon(AllIcons.Nodes.Class); 36 | 37 | completionResultSet.addElement(lookupElement); 38 | } 39 | return true; 40 | }, project); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/KNOWNBUGS.md: -------------------------------------------------------------------------------- 1 | **code completion in with does not work** 2 | ```php 3 | Person::find()->where(['id' => $id])->with('sentences.document', 'case') 4 | ``` 5 | **incorrect hasMany codecompletion in case viaTable** 6 | ```php 7 | return $this->hasMany(BvPerson::className(), ['id' => 'person_id']) 8 | ->viaTable('bv_company_person', ['company_id' => 'id']); 9 | ``` 10 | **Query::from does not detected** 11 | ```php 12 | $query->from('scoring'); 13 | ``` 14 | **Code completion incorrectly works for relations** 15 | ```php 16 | $model->getRelationName()->andWhere('') 17 | ``` 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/MissingActiveRecordInActiveQueryInspection.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.database; 2 | 3 | import com.intellij.codeInspection.ProblemHighlightType; 4 | import com.intellij.codeInspection.ProblemsHolder; 5 | import com.intellij.psi.PsiElementVisitor; 6 | import com.jetbrains.php.PhpIndex; 7 | import com.jetbrains.php.lang.inspections.PhpInspection; 8 | import com.jetbrains.php.lang.psi.elements.PhpClass; 9 | import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor; 10 | import com.nvlad.yii2support.common.ClassUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * Created by oleg on 08.04.2017. 15 | */ 16 | public class MissingActiveRecordInActiveQueryInspection extends PhpInspection { 17 | @NotNull 18 | @Override 19 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder problemsHolder, boolean isOnTheFly) { 20 | return new PhpElementVisitor() { 21 | @Override 22 | public void visitPhpClass(PhpClass clazz) { 23 | if (clazz.getSuperClass() != null && clazz.getSuperClass().getFQN().equals("\\yii\\db\\ActiveQuery")) { 24 | PhpIndex index = PhpIndex.getInstance(clazz.getProject()); 25 | 26 | PhpClass activeRecordClass = ClassUtils.findClassInSeeTags(index, clazz, "\\yii\\db\\BaseActiveRecord"); 27 | if (activeRecordClass == null) { 28 | problemsHolder.registerProblem(clazz.getFirstChild(), 29 | "Can not find connected ActiveRecord class.\nYou should add @see tag with linked ActiveRecord", 30 | ProblemHighlightType.WEAK_WARNING); 31 | } 32 | } 33 | super.visitPhpClass(clazz); 34 | 35 | } 36 | }; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/ParamsCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.database; 2 | 3 | 4 | import com.intellij.codeInsight.completion.CompletionType; 5 | import com.intellij.patterns.ElementPattern; 6 | import com.intellij.patterns.PlatformPatterns; 7 | import com.intellij.psi.PsiElement; 8 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class ParamsCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { 12 | public ParamsCompletionContributor() { 13 | extend(CompletionType.BASIC, ElementPattern(), new ParamsCompletionProvider()); 14 | } 15 | 16 | @Override 17 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 18 | if ((typeChar == '\'' || typeChar == '"') && position.getParent() instanceof ArrayCreationExpression) { 19 | return true; 20 | } 21 | 22 | return false; 23 | } 24 | 25 | private static ElementPattern ElementPattern() { 26 | return 27 | PlatformPatterns.or( 28 | PlatformPatterns.psiElement().withSuperParent(3, ArrayCreationExpression.class), 29 | PlatformPatterns.psiElement().withSuperParent(4, ArrayCreationExpression.class)); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/ParamsCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.database; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionProvider; 5 | import com.intellij.codeInsight.completion.CompletionResultSet; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.openapi.editor.Document; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.util.ProcessingContext; 10 | import com.jetbrains.php.lang.psi.elements.*; 11 | import com.nvlad.yii2support.common.ClassUtils; 12 | import com.nvlad.yii2support.common.DatabaseUtils; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * Created by oleg on 28.03.2017. 19 | */ 20 | public class ParamsCompletionProvider extends CompletionProvider { 21 | @Override 22 | protected void addCompletions(@NotNull CompletionParameters completionParameters, ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { 23 | MethodReference methodRef = ClassUtils.getMethodRef(completionParameters.getPosition(), 10); 24 | PsiElement position = completionParameters.getPosition(); 25 | if (methodRef != null) { 26 | Method method = (Method) methodRef.resolve(); 27 | int paramPosition = ClassUtils.indexForElementInParameterList(completionParameters.getPosition()); 28 | if (method != null && paramPosition > 0 && method.getParameters().length > paramPosition) { 29 | if (method.getParameters()[paramPosition].getName().equals("params") && 30 | ( method.getParameters()[paramPosition - 1].getName().equals("condition") || 31 | method.getParameters()[paramPosition - 1].getName().equals("sql") || 32 | method.getParameters()[paramPosition - 1].getName().equals("expression") )) { 33 | PsiElement element = methodRef.getParameters()[paramPosition - 1]; 34 | String condition = ClassUtils.getStringByElement(element); 35 | String[] result = DatabaseUtils.extractParamsFromCondition(condition); 36 | ArrayList usedItems = new ArrayList<>(); 37 | if (position.getParent().getParent().getParent() instanceof ArrayCreationExpression) { 38 | ArrayCreationExpression array = (ArrayCreationExpression) position.getParent().getParent().getParent(); 39 | for (ArrayHashElement elem : array.getHashElements()) { 40 | usedItems.add(ClassUtils.removeQuotes(elem.getKey().getText())); 41 | } 42 | } 43 | 44 | for (String resultItem : result) { 45 | if (!usedItems.contains(resultItem)) { 46 | LookupElementBuilder builder = LookupElementBuilder.create(resultItem).withInsertHandler((insertionContext, lookupElement) -> { 47 | 48 | Document document = insertionContext.getDocument(); 49 | int insertPosition = insertionContext.getSelectionEndOffset(); 50 | 51 | if (position.getParent().getParent().getParent() instanceof ArrayCreationExpression) { 52 | document.insertString(insertPosition + 1, " => "); 53 | insertPosition += 5; 54 | insertionContext.getEditor().getCaretModel().getCurrentCaret().moveToOffset(insertPosition); 55 | } 56 | }); 57 | completionResultSet.addElement(builder); 58 | } 59 | } 60 | 61 | 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/QueryCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.database; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionProvider; 5 | import com.intellij.codeInsight.completion.CompletionType; 6 | import com.intellij.openapi.util.Pair; 7 | import com.intellij.patterns.ElementPattern; 8 | import com.intellij.patterns.PlatformPatterns; 9 | import com.intellij.psi.PsiElement; 10 | import com.intellij.util.containers.MultiMap; 11 | import com.jetbrains.php.PhpIndex; 12 | import com.jetbrains.php.lang.psi.elements.*; 13 | import com.nvlad.yii2support.common.ClassUtils; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | public class QueryCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { 17 | private final MultiMap, CompletionProvider>> myMap = new MultiMap<>(); 18 | 19 | public QueryCompletionContributor() { 20 | extend(CompletionType.BASIC, ElementPattern(), new QueryCompletionProvider()); 21 | } 22 | 23 | @Override 24 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 25 | if ((typeChar == '\'' || typeChar == '"' || typeChar == '.') ) { 26 | MethodReference methodRef = ClassUtils.getMethodRef(position, 10); 27 | if (methodRef != null) { 28 | Method method = (Method)methodRef.resolve(); 29 | if (method != null) { 30 | Object possibleClass = method.getParent(); 31 | if (possibleClass instanceof PhpClass) { 32 | PhpIndex index = PhpIndex.getInstance(method.getProject()); 33 | if (ClassUtils.isClassInheritsOrEqual((PhpClass)possibleClass, ClassUtils.getClass(index, "\\yii\\db\\Query"), 100) || 34 | ClassUtils.isClassInheritsOrEqual((PhpClass)possibleClass, ClassUtils.getClass(index, "\\yii\\db\\Command"), 100) || 35 | ClassUtils.isClassInherit((PhpClass)possibleClass, ClassUtils.getClass(index, "\\yii\\db\\BaseActiveRecord")) || 36 | ClassUtils.isClassInheritsOrEqual((PhpClass)possibleClass, ClassUtils.getClass(index, "\\yii\\db\\Migration"), 100) 37 | ) { 38 | return true; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | 48 | private static ElementPattern ElementPattern() { 49 | 50 | return 51 | PlatformPatterns.or( 52 | // [" 53 | PlatformPatterns.psiElement().withSuperParent(3, ArrayCreationExpression.class), 54 | // string 55 | PlatformPatterns.psiElement().withSuperParent(3, MethodReference.class).withParent(StringLiteralExpression.class), 56 | // ["" => ""] 57 | PlatformPatterns.psiElement().withSuperParent(4, ArrayCreationExpression.class) 58 | 59 | ); 60 | 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/README.md: -------------------------------------------------------------------------------- 1 | **Database connection is required** 2 | 3 | ## ActiveQuery 4 | Code completion for ActiveQuery (ActiveRecord::find()->where for example) 5 | Code completion inside ActiveQuery linked to ActiveRecord 6 | Inspection in case if ActiveQuery not linked to ActiveRecord 7 | ## ActiveRecord 8 | Code completion for ActiveRecord findAll(), findOne() and so on methods 9 | Undetectable ActiveRecord table inspection 10 | Code completion in relations methods 11 | ## Migrations 12 | Code completion for migrations 13 | ## Condition parameters 14 | Condition parameters code completion 15 | Condition parameters inspection 16 | ## Properties 17 | Synchronize properties with database 18 | Unused properties inspection 19 | 20 | Beta version 0.4.17.0 (2017-04-09) 21 | Beta version 0.4.17.1 (2017-04-12) 22 | Beta version 0.4.17.2 (2017-04-18) 23 | Last version download: https://www.dropbox.com/s/41a8xvrjn7dqkaa/yii2support.jar?dl=1 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/TableInfo.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.database; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.intellij.database.model.DasColumn; 5 | import com.intellij.database.model.DasTable; 6 | import com.intellij.database.util.DasUtil; 7 | import com.intellij.util.containers.JBIterable; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Set; 13 | 14 | /** 15 | * Created by oleg on 23.03.2017. 16 | */ 17 | public class TableInfo { 18 | private final DasTable tableElement; 19 | 20 | private List columns = new ArrayList<>(); 21 | 22 | private List primaryKeys = new ArrayList<>(); 23 | 24 | public TableInfo(DasTable tableElement) { 25 | this.tableElement = tableElement; 26 | List columns = new ArrayList<>(); 27 | 28 | JBIterable columnsIter = DasUtil.getColumns(tableElement); 29 | List dasColumns = columnsIter.toList(); 30 | for (DasColumn dasColumn : dasColumns) { 31 | columns.add(dasColumn); 32 | 33 | if (DasUtil.isPrimary(dasColumn)) { 34 | primaryKeys.add(dasColumn.getName()); 35 | } 36 | } 37 | 38 | this.columns = columns; 39 | } 40 | 41 | public String getTableName() { 42 | return tableElement.getName(); 43 | } 44 | 45 | public List getColumns() { 46 | return columns; 47 | } 48 | 49 | public List getColumnsName() { 50 | List columnsName = Lists.newArrayList(); 51 | for (DasColumn column : columns) { 52 | columnsName.add(column.getName()); 53 | } 54 | return columnsName; 55 | } 56 | 57 | public List getPrimaryKeys() { 58 | return this.primaryKeys; 59 | } 60 | 61 | public List getNonPrimaryColumns() { 62 | Set pKNameSet = new HashSet<>(getPrimaryKeys()); 63 | 64 | List ret = new ArrayList<>(); 65 | for (DasColumn column : columns) { 66 | if (!pKNameSet.contains(column.getName())) { 67 | ret.add(column); 68 | } 69 | } 70 | 71 | return ret; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/UndetectableTableInspection.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.database; 2 | 3 | 4 | import com.intellij.codeInspection.ProblemHighlightType; 5 | import com.intellij.codeInspection.ProblemsHolder; 6 | import com.intellij.psi.PsiElementVisitor; 7 | import com.jetbrains.php.PhpIndex; 8 | import com.jetbrains.php.lang.inspections.PhpInspection; 9 | import com.jetbrains.php.lang.psi.elements.PhpClass; 10 | import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor; 11 | import com.nvlad.yii2support.common.ClassUtils; 12 | import com.nvlad.yii2support.common.DatabaseUtils; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | /** 16 | * Created by oleg on 06.04.2017. 17 | */ 18 | public class UndetectableTableInspection extends PhpInspection { 19 | @NotNull 20 | @Override 21 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder problemsHolder, boolean b) { 22 | return new PhpElementVisitor() { 23 | @Override 24 | public void visitPhpClass(PhpClass clazz) { 25 | if (clazz.isAbstract()) { 26 | return; 27 | } 28 | 29 | PhpIndex index = PhpIndex.getInstance(problemsHolder.getProject()); 30 | if (DatabaseUtils.HasConnections(problemsHolder.getProject()) && 31 | ClassUtils.isClassInheritsOrEqual(clazz, ClassUtils.getClass(index, "\\yii\\db\\ActiveRecord"), 100)) { 32 | String table = DatabaseUtils.getTableByActiveRecordClass(clazz); 33 | if (table == null) { 34 | problemsHolder.registerProblem(clazz.getFirstChild(), "Can not detect database table for class " + clazz.getFQN(), ProblemHighlightType.WEAK_WARNING); 35 | } else if (! DatabaseUtils.isTableExists(table, problemsHolder.getProject())) { 36 | problemsHolder.registerProblem(clazz.getFirstChild(), "Table '" + table + "' not found in database connections", ProblemHighlightType.WEAK_WARNING); 37 | } 38 | } 39 | 40 | super.visitPhpClass(clazz); 41 | } 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/database/settings/SettingsForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
82 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/errorreport/ErrorReportHandler.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.errorreport; 2 | 3 | import com.intellij.diagnostic.AbstractMessage; 4 | import com.intellij.openapi.diagnostic.ErrorReportSubmitter; 5 | import com.intellij.openapi.diagnostic.IdeaLoggingEvent; 6 | import com.intellij.openapi.diagnostic.SubmittedReportInfo; 7 | import com.intellij.util.Consumer; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.awt.*; 12 | 13 | public class ErrorReportHandler extends ErrorReportSubmitter { 14 | @NotNull 15 | @Override 16 | public String getReportActionText() { 17 | return "Report to Yii2 Support"; 18 | } 19 | 20 | @Override 21 | public boolean submit(@NotNull IdeaLoggingEvent [] events, @Nullable String additionalInfo, @NotNull Component parentComponent, @NotNull Consumer consumer) { 22 | /* for (IdeaLoggingEvent event : events) { 23 | Throwable throwable = event.getThrowable(); 24 | if (event.getData() instanceof AbstractMessage) { 25 | throwable = ((AbstractMessage) event.getData()).getThrowable(); 26 | } 27 | 28 | SentryErrorReporter.submitErrorReport(throwable, additionalInfo, consumer); 29 | }*/ 30 | 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/forms/FieldAttributesCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.forms; 2 | 3 | import com.intellij.codeInsight.completion.CompletionType; 4 | import com.intellij.patterns.ElementPattern; 5 | import com.intellij.patterns.PlatformPatterns; 6 | import com.intellij.psi.PsiElement; 7 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 8 | import com.jetbrains.php.lang.psi.elements.MethodReference; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * Created by oleg on 2017-12-18. 13 | */ 14 | public class FieldAttributesCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { 15 | public FieldAttributesCompletionContributor() { 16 | extend(CompletionType.BASIC, ElementPattern(), new FieldAttributesCompletionProvider()); 17 | } 18 | 19 | @Override 20 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 21 | if ((typeChar == '\'' || typeChar == '"') && position.getParent() instanceof MethodReference) { 22 | return true; 23 | } 24 | 25 | return false; 26 | } 27 | 28 | private static ElementPattern ElementPattern() { 29 | return 30 | PlatformPatterns.or( 31 | PlatformPatterns.psiElement().withSuperParent(3, ArrayCreationExpression.class), 32 | PlatformPatterns.psiElement().withSuperParent(4, ArrayCreationExpression.class)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/forms/FieldAttributesCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.forms; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionProvider; 5 | import com.intellij.codeInsight.completion.CompletionResultSet; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.openapi.editor.Document; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.util.ProcessingContext; 10 | import com.jetbrains.php.PhpIndex; 11 | import com.jetbrains.php.lang.psi.elements.*; 12 | import com.nvlad.yii2support.common.ClassUtils; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Collection; 16 | 17 | /** 18 | * Created by oleg on 2017-12-18. 19 | */ 20 | public class FieldAttributesCompletionProvider extends CompletionProvider { 21 | @Override 22 | 23 | protected void addCompletions(@NotNull CompletionParameters completionParameters, ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { 24 | MethodReference methodRef = ClassUtils.getMethodRef(completionParameters.getPosition(), 10); 25 | PsiElement position = completionParameters.getPosition(); 26 | if (methodRef != null) { 27 | Method method = (Method) methodRef.resolve(); 28 | int paramPosition = ClassUtils.indexForElementInParameterList(completionParameters.getPosition()); 29 | // attribute plus model parameters 30 | if (method != null && paramPosition > 0 && method.getParameters().length > paramPosition) { 31 | if (method.getParameters()[paramPosition].getName().equals("attribute") && 32 | method.getParameters()[paramPosition - 1].getName().equals("model")) { 33 | PsiElement element = methodRef.getParameters()[paramPosition - 1]; 34 | if (element instanceof Variable) { 35 | PhpClass classByVariable = ClassUtils.getClassByVariable((Variable) element); 36 | if (classByVariable != null && 37 | ClassUtils.isClassInherit(classByVariable, "yii\\base\\Model", PhpIndex.getInstance(position.getProject()) )) { 38 | Collection classFields = ClassUtils.getClassFields(classByVariable); 39 | PhpExpression position2 = (PhpExpression) completionParameters.getPosition().getParent(); 40 | for (Field field : classFields) { 41 | LookupElementBuilder lookupBuilder = buildLookup(field, position2); 42 | completionResultSet.addElement(lookupBuilder); 43 | } 44 | } 45 | } 46 | 47 | } 48 | } 49 | } 50 | } 51 | 52 | @NotNull 53 | private LookupElementBuilder buildLookup(PhpClassMember field, PhpExpression position) { 54 | String lookupString = field instanceof Method ? ClassUtils.getAsPropertyName((Method) field) : field.getName(); 55 | LookupElementBuilder builder = LookupElementBuilder.create(field, lookupString).withIcon(field.getIcon()) 56 | .withInsertHandler((insertionContext, lookupElement) -> { 57 | 58 | Document document = insertionContext.getDocument(); 59 | int insertPosition = insertionContext.getSelectionEndOffset(); 60 | 61 | if (position.getParent().getParent() instanceof ArrayCreationExpression) { 62 | document.insertString(insertPosition + 1, " => "); 63 | insertPosition += 5; 64 | insertionContext.getEditor().getCaretModel().getCurrentCaret().moveToOffset(insertPosition); 65 | } 66 | }); 67 | if (field instanceof Field) { 68 | builder = builder.withTypeText(field.getType().toString()); 69 | } 70 | return builder; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/i18n/CategoryLookupElement.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.i18n; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementPresentation; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiFile; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Created by NVlad on 06.01.2017. 11 | */ 12 | class CategoryLookupElement extends LookupElement { 13 | final private PsiElement myCategory; 14 | 15 | CategoryLookupElement(PsiElement category) { 16 | myCategory = category; 17 | } 18 | 19 | @NotNull 20 | @Override 21 | public String getLookupString() { 22 | if (myCategory instanceof PsiFile) { 23 | String filename = ((PsiFile) myCategory).getName(); 24 | return filename.substring(0, filename.lastIndexOf(".")); 25 | } 26 | 27 | return myCategory.getText(); 28 | } 29 | 30 | @Override 31 | public void renderElement(LookupElementPresentation presentation) { 32 | super.renderElement(presentation); 33 | 34 | if (myCategory instanceof PsiFile) { 35 | PsiFile file = (PsiFile) myCategory; 36 | String filename = file.getName(); 37 | presentation.setIcon(file.getIcon(0)); 38 | presentation.setItemText(filename.substring(0, filename.lastIndexOf("."))); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/i18n/CompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.i18n; 2 | 3 | import com.intellij.codeInsight.completion.CompletionType; 4 | import com.intellij.patterns.ElementPattern; 5 | import com.intellij.patterns.PlatformPatterns; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 8 | import com.intellij.psi.util.PsiTreeUtil; 9 | import com.jetbrains.php.lang.psi.elements.ClassReference; 10 | import com.jetbrains.php.lang.psi.elements.MethodReference; 11 | import com.jetbrains.php.lang.psi.elements.ParameterList; 12 | import com.nvlad.yii2support.common.Patterns; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | /** 16 | * Created by NVlad on 06.01.2017. 17 | */ 18 | public class CompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { 19 | public CompletionContributor() { 20 | extend(CompletionType.BASIC, ElementPattern(), new CompletionProvider()); 21 | } 22 | 23 | @Override 24 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 25 | MethodReference reference = PsiTreeUtil.getParentOfType(position, MethodReference.class); 26 | if (reference != null && reference.getName() != null && reference.getName().equals("t") && reference.getClassReference() instanceof ClassReference) { 27 | ClassReference classReference = (ClassReference) reference.getClassReference(); 28 | if (classReference == null || classReference.getName() == null || !classReference.getName().equals("Yii")) { 29 | return false; 30 | } 31 | if (typeChar == '\'' || typeChar == '"') { 32 | if (position instanceof LeafPsiElement && (position.getText().equals("$category") || position.getText().equals("$message"))) { 33 | return true; 34 | } 35 | if (position.getNextSibling() instanceof ParameterList) { 36 | return true; 37 | } 38 | } 39 | } 40 | 41 | return false; 42 | } 43 | 44 | private static ElementPattern ElementPattern() { 45 | return PlatformPatterns.psiElement(PsiElement.class) 46 | .withSuperParent(3, Patterns.methodWithName("t")); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/i18n/CompletionProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.i18n; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionResultSet; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.util.ProcessingContext; 7 | import com.jetbrains.php.lang.psi.elements.*; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * Created by NVlad on 06.01.2017. 12 | */ 13 | class CompletionProvider extends com.intellij.codeInsight.completion.CompletionProvider { 14 | @Override 15 | protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) { 16 | PhpPsiElement psiElement = (PhpPsiElement) parameters.getPosition().getParent(); 17 | 18 | MethodReference methodReference = (MethodReference) psiElement.getParent().getParent(); 19 | PhpExpression classReference = methodReference.getClassReference(); 20 | if (classReference != null && classReference.getName() != null) { 21 | if (methodReference.isStatic() && classReference.getName().equals("Yii")) { 22 | String methodName = methodReference.getName(); 23 | if (methodName != null && methodReference.getParameterList() != null) { 24 | PsiElement[] methodParameters = methodReference.getParameterList().getParameters(); 25 | 26 | int parameterIndex = -1; 27 | for (int i = 0; i < methodParameters.length; i++) { 28 | if (psiElement == methodParameters[i]) { 29 | parameterIndex = i; 30 | break; 31 | } 32 | } 33 | 34 | switch (parameterIndex) { 35 | case 0: 36 | fillCategories(psiElement, result); 37 | break; 38 | case 1: 39 | if (methodParameters[0] instanceof StringLiteralExpression) { 40 | String category = ((StringLiteralExpression) methodParameters[0]).getContents(); 41 | fillMessages(psiElement, category, result); 42 | } 43 | break; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | private void fillCategories(PhpPsiElement element, CompletionResultSet result) { 51 | for (PsiElement category : Util.getCategories(element)) { 52 | result.addElement(new CategoryLookupElement(category)); 53 | } 54 | } 55 | 56 | private void fillMessages(PhpPsiElement element, String category, CompletionResultSet result) { 57 | for (ArrayHashElement message : Util.getMessages(element, category)) { 58 | result.addElement(new MessageLookupElement(element, message)); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/actions/OrderAscAction.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.actions; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.Toggleable; 6 | import com.intellij.openapi.application.ApplicationManager; 7 | import com.intellij.ui.AnActionButton; 8 | import com.nvlad.yii2support.migrations.ui.toolWindow.MigrationPanel; 9 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 10 | 11 | @SuppressWarnings("ComponentNotRegistered") 12 | public class OrderAscAction extends AnActionButton implements Toggleable { 13 | public OrderAscAction() { 14 | super("Newest migrations first", AllIcons.RunConfigurations.SortbyDuration); 15 | 16 | // if (SystemInfo.isMac) { 17 | // setShortcut(CustomShortcutSet.fromString("meta S")); 18 | // } else { 19 | // setShortcut(CustomShortcutSet.fromString("ctrl S")); 20 | // } 21 | } 22 | 23 | @Override 24 | public void actionPerformed(AnActionEvent anActionEvent) { 25 | final Yii2SupportSettings settings = Yii2SupportSettings.getInstance(anActionEvent.getProject()); 26 | settings.newestFirst = !settings.newestFirst; 27 | anActionEvent.getPresentation().putClientProperty(SELECTED_PROPERTY, settings.newestFirst); 28 | 29 | MigrationPanel panel = (MigrationPanel) getContextComponent(); 30 | ApplicationManager.getApplication().invokeLater(() -> { 31 | panel.updateTree(); 32 | panel.updateUI(); 33 | }); 34 | } 35 | 36 | @Override 37 | public void updateButton(AnActionEvent e) { 38 | final Yii2SupportSettings settings = Yii2SupportSettings.getInstance(e.getProject()); 39 | e.getPresentation().putClientProperty(SELECTED_PROPERTY, settings.newestFirst); 40 | 41 | super.updateButton(e); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/actions/RefreshAction.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.actions; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.project.Project; 6 | import com.nvlad.yii2support.migrations.commands.CommandBase; 7 | import com.nvlad.yii2support.migrations.commands.MigrationHistory; 8 | import com.nvlad.yii2support.migrations.entities.DefaultMigrateCommand; 9 | import com.nvlad.yii2support.migrations.entities.MigrateCommand; 10 | import com.nvlad.yii2support.migrations.entities.Migration; 11 | import com.nvlad.yii2support.migrations.services.MigrationService; 12 | import kotlin.reflect.jvm.internal.impl.utils.SmartList; 13 | 14 | import java.util.*; 15 | 16 | @SuppressWarnings("ComponentNotRegistered") 17 | public class RefreshAction extends MigrateBaseAction { 18 | public RefreshAction() { 19 | super("Refresh migrations", AllIcons.Actions.Refresh); 20 | } 21 | 22 | @Override 23 | public void actionPerformed(AnActionEvent event) { 24 | Project project = event.getProject(); 25 | if (project == null) { 26 | return; 27 | } 28 | 29 | MigrationService service = MigrationService.getInstance(project); 30 | service.sync(); 31 | 32 | Map> migrateCommandMap = new HashMap<>(); 33 | MigrateCommand defaultCommand = null; 34 | for (MigrateCommand command : service.getMigrationCommandMap().keySet()) { 35 | migrateCommandMap.put(command, new HashSet<>()); 36 | if (command.isDefault) { 37 | defaultCommand = command; 38 | } 39 | } 40 | 41 | for (Migration migration : service.getMigrations()) { 42 | for (MigrateCommand command : migrateCommandMap.keySet()) { 43 | if (command.containsMigration(project, migration)) { 44 | if (command instanceof DefaultMigrateCommand) { 45 | migrateCommandMap.get(defaultCommand).add(migration); 46 | } else { 47 | migrateCommandMap.get(command).add(migration); 48 | } 49 | } 50 | } 51 | } 52 | 53 | List commands = new SmartList<>(); 54 | for (MigrateCommand command : migrateCommandMap.keySet()) { 55 | if (migrateCommandMap.get(command).isEmpty()) { 56 | continue; 57 | } 58 | 59 | commands.add(new MigrationHistory(project, command, new ArrayList<>(migrateCommandMap.get(command)))); 60 | } 61 | 62 | executeCommand(project, commands); 63 | } 64 | 65 | @Override 66 | public boolean isEnabled() { 67 | return getTree().isEnabled(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/commands/MigrationDown.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.commands; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.nvlad.yii2support.migrations.entities.MigrateCommand; 5 | import com.nvlad.yii2support.migrations.entities.Migration; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | public class MigrationDown extends CommandUpDownRedoBase { 12 | public MigrationDown(@NotNull Project project, @NotNull List migrations, @NotNull MigrateCommand command, String path) { 13 | super(project, migrations, command, path); 14 | direction = "reverting"; 15 | } 16 | 17 | @Override 18 | public void run() { 19 | LinkedList params = new LinkedList<>(); 20 | params.add(String.valueOf(myMigrations.size())); 21 | prepareCommandParams(params, myCommand, myPath); 22 | executeActionWithParams("down", params); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/commands/MigrationRedo.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.commands; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.nvlad.yii2support.migrations.entities.MigrateCommand; 5 | import com.nvlad.yii2support.migrations.entities.Migration; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | public class MigrationRedo extends CommandUpDownRedoBase { 12 | public MigrationRedo(@NotNull Project project, @NotNull List migrations, @NotNull MigrateCommand command, String path) { 13 | super(project, migrations, command, path); 14 | direction = "reverting"; 15 | } 16 | 17 | @Override 18 | public void run() { 19 | LinkedList params = new LinkedList<>(); 20 | params.add(String.valueOf(myMigrations.size())); 21 | prepareCommandParams(params, myCommand, myPath); 22 | executeActionWithParams("redo", params); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/commands/MigrationUp.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.commands; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.nvlad.yii2support.migrations.entities.MigrateCommand; 5 | import com.nvlad.yii2support.migrations.entities.Migration; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | 11 | public class MigrationUp extends CommandUpDownRedoBase { 12 | public MigrationUp(@NotNull Project project, @NotNull List migrations, @NotNull MigrateCommand command, String path) { 13 | super(project, migrations, command, path); 14 | direction = "applying"; 15 | } 16 | 17 | @Override 18 | public void run() { 19 | LinkedList params = new LinkedList<>(); 20 | params.add(String.valueOf(myMigrations.size())); 21 | prepareCommandParams(params, myCommand, myPath); 22 | executeActionWithParams("up", params); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/entities/DefaultMigrateCommand.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.entities; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.List; 7 | 8 | public class DefaultMigrateCommand extends MigrateCommand { 9 | public DefaultMigrateCommand(List commands) { 10 | super(); 11 | 12 | for (MigrateCommand migrateCommand : commands) { 13 | if (migrateCommand.isDefault) { 14 | command = migrateCommand.command; 15 | migrationTable = migrateCommand.migrationTable; 16 | db = migrateCommand.db; 17 | useTablePrefix = migrateCommand.useTablePrefix; 18 | } 19 | } 20 | } 21 | 22 | @Override 23 | public boolean containsMigration(Project project, Migration migration) { 24 | return true; 25 | } 26 | 27 | @Override 28 | public int compareTo(@NotNull MigrateCommand command) { 29 | return -1; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/entities/MigrateCommand.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.entities; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.text.StringUtil; 5 | import com.intellij.util.SmartList; 6 | import com.nvlad.yii2support.common.YiiAlias; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.List; 10 | 11 | public class MigrateCommand implements Comparable, Cloneable { 12 | public boolean isDefault; 13 | public String command; 14 | public String migrationTable; 15 | public String db; 16 | public List migrationPath; 17 | public List migrationNamespaces; 18 | public boolean useTablePrefix; 19 | 20 | private List myPathCache; 21 | 22 | public MigrateCommand() { 23 | isDefault = false; 24 | migrationPath = new SmartList<>(); 25 | migrationNamespaces = new SmartList<>(); 26 | useTablePrefix = false; 27 | } 28 | 29 | @Override 30 | public int compareTo(@NotNull MigrateCommand command) { 31 | if (this.isDefault) { 32 | return 1; 33 | } 34 | 35 | if (command.isDefault) { 36 | return -1; 37 | } 38 | 39 | return this.command.compareTo(command.command); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (!(obj instanceof MigrateCommand)) { 45 | return false; 46 | } 47 | 48 | if (obj == this) { 49 | return true; 50 | } 51 | 52 | MigrateCommand options = (MigrateCommand) obj; 53 | 54 | return options.isDefault == isDefault 55 | && options.useTablePrefix == useTablePrefix 56 | && StringUtil.equals(options.command, command) 57 | && StringUtil.equals(options.migrationTable, migrationTable) 58 | && StringUtil.equals(options.db, db) 59 | && options.migrationPath.equals(migrationPath) 60 | && options.migrationNamespaces.equals(migrationNamespaces); 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return (isDefault ? 1 : 0) 66 | + command.hashCode() 67 | + migrationTable.hashCode() 68 | + db.hashCode() 69 | + migrationPath.hashCode() 70 | + migrationNamespaces.hashCode() 71 | + (useTablePrefix ? 1 : 0); 72 | } 73 | 74 | @Override 75 | public MigrateCommand clone() { 76 | MigrateCommand clone = new MigrateCommand(); 77 | clone.isDefault = isDefault; 78 | clone.command = command; 79 | clone.migrationTable = migrationTable; 80 | clone.db = db; 81 | clone.migrationPath = new SmartList<>(migrationPath); 82 | clone.migrationNamespaces = new SmartList<>(migrationNamespaces); 83 | clone.useTablePrefix = useTablePrefix; 84 | 85 | return clone; 86 | } 87 | 88 | public boolean containsMigration(Project project, Migration migration) { 89 | if (getPathCache(project).contains(migration.path)) { 90 | return true; 91 | } 92 | 93 | return migrationNamespaces.contains(migration.namespace); 94 | } 95 | 96 | private List getPathCache(Project project) { 97 | if (myPathCache == null) { 98 | YiiAlias yiiAlias = YiiAlias.getInstance(project); 99 | myPathCache = new SmartList<>(); 100 | for (String path : migrationPath) { 101 | String resolvedAlias = yiiAlias.resolveAlias(path, true); 102 | if (resolvedAlias == null) { 103 | continue; 104 | } 105 | 106 | myPathCache.add(yiiAlias.resolveAlias(resolvedAlias, true)); 107 | } 108 | } 109 | 110 | return myPathCache; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/entities/MigrateCommandComparator.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.entities; 2 | 3 | import java.util.Comparator; 4 | 5 | public class MigrateCommandComparator implements Comparator { 6 | @Override 7 | public int compare(MigrateCommand o1, MigrateCommand o2) { 8 | if (o1.isDefault || o2 instanceof DefaultMigrateCommand) { 9 | return -1; 10 | } 11 | 12 | if (o2.isDefault || o1 instanceof DefaultMigrateCommand) { 13 | return 1; 14 | } 15 | 16 | return o1.command.compareTo(o2.command); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/entities/Migration.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.entities; 2 | 3 | import com.jetbrains.php.lang.psi.elements.PhpClass; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.security.InvalidParameterException; 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | import java.time.Duration; 11 | import java.util.Date; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | public class Migration implements Comparable { 16 | public PhpClass migrationClass; 17 | public final String name; 18 | public final String path; 19 | public final String namespace; 20 | public MigrationStatus status; 21 | public Date createdAt; 22 | public Date applyAt; 23 | public Duration downDuration; 24 | public Duration upDuration; 25 | 26 | private static final Pattern dateFromName = Pattern.compile("([mM])(\\d{6}_?\\d{6})\\D.+"); 27 | private static final SimpleDateFormat migrationPathDateFormat = new SimpleDateFormat("yyMMdd_HHmmss"); 28 | private static final SimpleDateFormat migrationNamespaceDateFormat = new SimpleDateFormat("yyMMddHHmmss"); 29 | 30 | public Migration(PhpClass clazz, String path) { 31 | this.migrationClass = clazz; 32 | this.name = clazz.getName(); 33 | this.path = path; 34 | this.namespace = clazz.getNamespaceName(); 35 | this.status = MigrationStatus.Unknown; 36 | this.createdAt = dateFromName(this.name); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return name; 42 | } 43 | 44 | @Override 45 | public int compareTo(@NotNull Migration migration) { 46 | return createdAt.compareTo(migration.createdAt); 47 | } 48 | 49 | public static boolean isValidMigrationClass(PhpClass phpClass) { 50 | return dateFromName.matcher(phpClass.getName()).find(); 51 | } 52 | 53 | @Nullable 54 | private static Date dateFromName(String name) { 55 | Matcher matcher = dateFromName.matcher(name); 56 | if (!matcher.find()) { 57 | throw new InvalidParameterException("Migration name <" + name + "> not contain or invalid creation date."); 58 | } 59 | 60 | try { 61 | if (matcher.group(1).equals("M")) { 62 | return migrationNamespaceDateFormat.parse(matcher.group(2)); 63 | } 64 | 65 | return migrationPathDateFormat.parse(matcher.group(2)); 66 | } catch (ParseException e) { 67 | throw new InvalidParameterException("Migration name <" + name + "> contain invalid creation date."); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/entities/MigrationComparator.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.entities; 2 | 3 | import java.util.Comparator; 4 | 5 | public class MigrationComparator implements Comparator { 6 | private final boolean myNewestFirst; 7 | 8 | public MigrationComparator(boolean newestFirst) { 9 | myNewestFirst = newestFirst; 10 | } 11 | 12 | @Override 13 | public int compare(Migration m1, Migration m2) { 14 | if (myNewestFirst) { 15 | return m2.createdAt.compareTo(m1.createdAt); 16 | } 17 | 18 | return m1.createdAt.compareTo(m2.createdAt); 19 | } 20 | 21 | @Override 22 | public boolean equals(Object obj) { 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/entities/MigrationStatus.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.entities; 2 | 3 | public enum MigrationStatus { 4 | Progress, 5 | Unknown, 6 | NotApply, 7 | Success, 8 | ApplyError, 9 | RollbackError, 10 | } 11 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/services/MigrationServiceListener.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.services; 2 | 3 | public interface MigrationServiceListener { 4 | void treeChanged(); 5 | } 6 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/services/MigrationsVirtualFileMonitor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.services; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.application.ReadAction; 5 | import com.intellij.openapi.project.DumbService; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.vfs.*; 8 | import com.intellij.psi.PsiFile; 9 | import com.intellij.psi.PsiManager; 10 | import com.jetbrains.php.PhpIndex; 11 | import com.jetbrains.php.codeInsight.controlFlow.instructions.PhpClassDeclarationInstruction; 12 | import com.jetbrains.php.codeInsight.controlFlow.instructions.PhpInstruction; 13 | import com.jetbrains.php.lang.psi.PhpFile; 14 | import com.jetbrains.php.lang.psi.elements.PhpClass; 15 | import com.nvlad.yii2support.common.ClassUtils; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | public class MigrationsVirtualFileMonitor implements VirtualFileListener { 19 | private final Project myProject; 20 | private final MigrationService service; 21 | 22 | public MigrationsVirtualFileMonitor(Project project) { 23 | myProject = project; 24 | service = MigrationService.getInstance(project); 25 | } 26 | 27 | @Override 28 | public void propertyChanged(@NotNull VirtualFilePropertyEvent virtualFilePropertyEvent) { 29 | 30 | } 31 | 32 | @Override 33 | public void contentsChanged(@NotNull VirtualFileEvent virtualFileEvent) { 34 | // MigrationService service = MigrationService.getInstance(myProject); 35 | // service.findMigrationByFile(virtualFileEvent.getFile()); 36 | } 37 | 38 | @Override 39 | public void fileCreated(@NotNull VirtualFileEvent event) { 40 | ApplicationManager.getApplication().executeOnPooledThread( 41 | () -> ApplicationManager.getApplication().runReadAction( 42 | () -> { 43 | if (isMigrationFile(event)) { 44 | service.sync(); 45 | } 46 | } 47 | ) 48 | ); 49 | } 50 | 51 | @Override 52 | public void fileDeleted(@NotNull VirtualFileEvent event) { 53 | ApplicationManager.getApplication().executeOnPooledThread(service::sync); 54 | } 55 | 56 | @Override 57 | public void fileMoved(@NotNull VirtualFileMoveEvent virtualFileMoveEvent) { 58 | 59 | } 60 | 61 | @Override 62 | public void fileCopied(@NotNull VirtualFileCopyEvent virtualFileCopyEvent) { 63 | 64 | } 65 | 66 | @Override 67 | public void beforePropertyChange(@NotNull VirtualFilePropertyEvent virtualFilePropertyEvent) { 68 | 69 | } 70 | 71 | @Override 72 | public void beforeContentsChange(@NotNull VirtualFileEvent virtualFileEvent) { 73 | 74 | } 75 | 76 | @Override 77 | public void beforeFileDeletion(@NotNull VirtualFileEvent virtualFileEvent) { 78 | 79 | } 80 | 81 | @Override 82 | public void beforeFileMovement(@NotNull VirtualFileMoveEvent virtualFileMoveEvent) { 83 | 84 | } 85 | 86 | private boolean isMigrationFile(VirtualFileEvent event) { 87 | VirtualFile virtualFile = event.getFile(); 88 | PsiFile psiFile = PsiManager.getInstance(myProject).findFile(virtualFile); 89 | if (!(psiFile instanceof PhpFile)) { 90 | return false; 91 | } 92 | 93 | for (PhpInstruction instruction : ((PhpFile) psiFile).getControlFlow().getInstructions()) { 94 | if (instruction instanceof PhpClassDeclarationInstruction) { 95 | PhpClass phpClass = ((PhpClassDeclarationInstruction) instruction).getClassDeclaration(); 96 | if (phpClass.isAbstract()) { 97 | continue; 98 | } 99 | 100 | if(DumbService.getInstance(myProject).isDumb()){ 101 | DumbService.getInstance(myProject).runWhenSmart(() -> this.fileCreated(event)); 102 | return false; 103 | } 104 | PhpIndex phpIndex = PhpIndex.getInstance(myProject); 105 | if (ClassUtils.isClassInheritsOrEqual(phpClass, "\\yii\\db\\Migration", phpIndex)) { 106 | return true; 107 | } 108 | } 109 | } 110 | 111 | return false; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/settings/ComboBoxTableCellEditor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.settings; 2 | 3 | import com.intellij.openapi.ui.ComboBox; 4 | import com.intellij.util.ui.AbstractTableCellEditor; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.awt.event.ItemEvent; 9 | import java.util.List; 10 | 11 | public class ComboBoxTableCellEditor extends AbstractTableCellEditor { 12 | private final ComboBox myEditorComponent; 13 | private final List myCompletionList; 14 | 15 | public ComboBoxTableCellEditor(List completionList) { 16 | myEditorComponent = new ComboBox<>(); 17 | myCompletionList = completionList; 18 | 19 | myEditorComponent.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); 20 | } 21 | 22 | @Override 23 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 24 | if (!isSelected) { 25 | return null; 26 | } 27 | 28 | DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel<>(); 29 | if (myCompletionList != null) { 30 | for (String item: myCompletionList) { 31 | comboBoxModel.addElement(item); 32 | } 33 | } 34 | 35 | comboBoxModel.setSelectedItem(value); 36 | 37 | myEditorComponent.setModel(comboBoxModel); 38 | myEditorComponent.setEditable(true); 39 | myEditorComponent.addItemListener(e -> { 40 | if (e.getStateChange() != ItemEvent.SELECTED) { 41 | return; 42 | } 43 | 44 | int index = comboBoxModel.getIndexOf(e.getItem()); 45 | if (index == -1) { 46 | comboBoxModel.addElement((String) e.getItem()); 47 | } 48 | }); 49 | 50 | return myEditorComponent; 51 | } 52 | 53 | @Override 54 | public Object getCellEditorValue() { 55 | return myEditorComponent.getEditor().getItem(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/settings/MigrateCommandTableModel.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.settings; 2 | 3 | import com.intellij.ui.AddEditRemovePanel; 4 | import com.nvlad.yii2support.migrations.entities.MigrateCommand; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public class MigrateCommandTableModel extends AddEditRemovePanel.TableModel { 8 | private final String[] columnNames = new String[]{"Command", "Table", "Component"}; 9 | 10 | @Override 11 | public int getColumnCount() { 12 | return 3; 13 | } 14 | 15 | @Nullable 16 | @Override 17 | public String getColumnName(int i) { 18 | return columnNames[i]; 19 | } 20 | 21 | @Override 22 | public Object getField(MigrateCommand command, int i) { 23 | switch (i) { 24 | case 0: 25 | return command.command; 26 | case 1: 27 | return command.migrationTable; 28 | case 2: 29 | return command.db; 30 | } 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/settings/MigrationCommandDialogValidator.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.settings; 2 | 3 | public interface MigrationCommandDialogValidator { 4 | boolean hasSave(MigrationCommandDialog dialog); 5 | } 6 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/settings/MigrationPanel.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.settings; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.Messages; 5 | import com.intellij.ui.AddEditRemovePanel; 6 | import com.nvlad.yii2support.migrations.entities.MigrateCommand; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import javax.swing.*; 10 | import javax.swing.table.DefaultTableCellRenderer; 11 | import javax.swing.table.JTableHeader; 12 | import javax.swing.table.TableCellRenderer; 13 | import java.awt.*; 14 | import java.util.List; 15 | 16 | public class MigrationPanel extends AddEditRemovePanel { 17 | final private Project myProject; 18 | 19 | public MigrationPanel(Project project, List commandList) { 20 | super(new MigrateCommandTableModel(), commandList); 21 | 22 | myProject = project; 23 | getTable().setTableHeader(new JTableHeader(getTable().getColumnModel())); 24 | TableCellRenderer renderer = new DefaultTableCellRenderer() { 25 | @Override 26 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 27 | Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 28 | if (getData().get(row).isDefault) { 29 | component.setFont(component.getFont().deriveFont(Font.BOLD)); 30 | } 31 | 32 | return component; 33 | } 34 | }; 35 | 36 | setRenderer(0, renderer); 37 | setRenderer(1, renderer); 38 | setRenderer(2, renderer); 39 | } 40 | 41 | @Nullable 42 | @Override 43 | protected MigrateCommand addItem() { 44 | return showEditor(myProject, null); 45 | } 46 | 47 | @Override 48 | protected boolean removeItem(MigrateCommand command) { 49 | if (command.isDefault) { 50 | Messages.showErrorDialog("Do not remove default migrate command.", "Error"); 51 | 52 | return false; 53 | } 54 | 55 | return true; 56 | } 57 | 58 | @Nullable 59 | @Override 60 | protected MigrateCommand editItem(MigrateCommand command) { 61 | return showEditor(myProject, command); 62 | } 63 | 64 | @Nullable 65 | private MigrateCommand showEditor(Project project, MigrateCommand command) { 66 | MigrationCommandDialog dialog = new MigrationCommandDialog(project); 67 | 68 | dialog.addValidator(optionsDialog -> { 69 | if (optionsDialog.isNewEntry()) { 70 | final String commandName = optionsDialog.getCommandName(); 71 | return getData().stream().noneMatch(c -> commandName.equals(c.command)); 72 | } 73 | 74 | return true; 75 | }); 76 | 77 | dialog.setEntry(command); 78 | dialog.show(); 79 | 80 | if (dialog.isOK()) { 81 | MigrateCommand entry = dialog.getEntry(); 82 | if (entry.isDefault) { 83 | for (MigrateCommand migrateCommand : getData()) { 84 | if (migrateCommand.isDefault) { 85 | migrateCommand.isDefault = false; 86 | break; 87 | } 88 | } 89 | } 90 | 91 | return entry; 92 | } 93 | 94 | return null; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/settings/StringListEditPanel.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.settings; 2 | 3 | import com.intellij.ui.AddEditRemovePanel; 4 | import com.intellij.util.SmartList; 5 | import com.nvlad.yii2support.migrations.ui.settings.entities.TableModelStringEntity; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.table.TableColumn; 9 | import java.util.List; 10 | 11 | public class StringListEditPanel extends AddEditRemovePanel { 12 | 13 | StringListEditPanel(String label, List completionList) { 14 | super(new StringListTableModel(), new SmartList<>(), label); 15 | 16 | TableColumn tableColumn = getTable().getColumnModel().getColumn(0); 17 | tableColumn.setCellEditor(new ComboBoxTableCellEditor(completionList)); 18 | } 19 | 20 | public void setStringData(List data) { 21 | setData(TableModelStringEntity.fromList(data)); 22 | } 23 | 24 | public List getStringData() { 25 | return TableModelStringEntity.toStringList(getData()); 26 | } 27 | 28 | @Nullable 29 | @Override 30 | protected TableModelStringEntity addItem() { 31 | return new TableModelStringEntity(""); 32 | } 33 | 34 | @Override 35 | protected boolean removeItem(TableModelStringEntity s) { 36 | return true; 37 | } 38 | 39 | @Nullable 40 | @Override 41 | protected TableModelStringEntity editItem(TableModelStringEntity s) { 42 | int index = getData().indexOf(s); 43 | getTable().editCellAt(index, 0); 44 | return s; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/settings/StringListTableModel.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.settings; 2 | 3 | import com.nvlad.yii2support.migrations.ui.settings.entities.TableModelStringEntity; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class StringListTableModel extends com.intellij.ui.AddEditRemovePanel.TableModel { 7 | @Override 8 | public int getColumnCount() { 9 | return 1; 10 | } 11 | 12 | @Nullable 13 | @Override 14 | public String getColumnName(int i) { 15 | return null; 16 | } 17 | 18 | @Override 19 | public Object getField(TableModelStringEntity tableModelStringEntity, int i) { 20 | return tableModelStringEntity.getValue(); 21 | } 22 | 23 | @Override 24 | public boolean isEditable(int column) { 25 | return true; 26 | } 27 | 28 | @Override 29 | public void setValue(Object aValue, TableModelStringEntity data, int columnIndex) { 30 | data.setValue((String) aValue); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/settings/entities/TableModelStringEntity.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.settings.entities; 2 | 3 | import java.util.List; 4 | import java.util.Vector; 5 | 6 | public class TableModelStringEntity { 7 | private String myString; 8 | 9 | public TableModelStringEntity(String item) { 10 | myString = item; 11 | } 12 | 13 | public void setValue(String value) { 14 | myString = value; 15 | } 16 | 17 | public String getValue() { 18 | return myString; 19 | } 20 | 21 | public static List fromList(List items) { 22 | List result = new Vector<>(); 23 | for (String item: items) { 24 | result.add(new TableModelStringEntity(item)); 25 | } 26 | 27 | return result; 28 | } 29 | 30 | public static List toStringList(List items) { 31 | List result = new Vector<>(); 32 | for (TableModelStringEntity item: items) { 33 | result.add(item.getValue()); 34 | } 35 | 36 | return result; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/toolWindow/ConsolePanel.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.toolWindow; 2 | 3 | import com.intellij.execution.impl.ConsoleViewImpl; 4 | import com.intellij.execution.ui.ConsoleView; 5 | import com.intellij.openapi.actionSystem.ActionManager; 6 | import com.intellij.openapi.actionSystem.ActionToolbar; 7 | import com.intellij.openapi.actionSystem.DefaultActionGroup; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.ui.SimpleToolWindowPanel; 10 | 11 | import javax.swing.*; 12 | 13 | public class ConsolePanel extends SimpleToolWindowPanel { 14 | private final ConsoleView myConsoleView; 15 | 16 | public ConsolePanel(Project project) { 17 | super(false); 18 | 19 | myConsoleView = new ConsoleViewImpl(project, true); 20 | final JComponent consoleViewComponent = myConsoleView.getComponent(); 21 | final DefaultActionGroup actionGroup = new DefaultActionGroup(); 22 | final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("MigrationsConsole", actionGroup, false); 23 | 24 | actionGroup.addAll(myConsoleView.createConsoleActions()); 25 | 26 | setContent(consoleViewComponent); 27 | setToolbar(toolbar.getComponent()); 28 | } 29 | 30 | public ConsoleView getConsoleView() { 31 | return myConsoleView; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/ui/toolWindow/MigrationsMouseListener.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.ui.toolWindow; 2 | 3 | import com.nvlad.yii2support.migrations.entities.MigrateCommand; 4 | import com.nvlad.yii2support.migrations.entities.Migration; 5 | 6 | import javax.swing.*; 7 | import javax.swing.tree.DefaultMutableTreeNode; 8 | import javax.swing.tree.TreePath; 9 | import java.awt.event.MouseEvent; 10 | import java.awt.event.MouseListener; 11 | 12 | public class MigrationsMouseListener implements MouseListener { 13 | @Override 14 | public void mouseClicked(MouseEvent e) { 15 | if (e.getClickCount() == 2) { 16 | JTree migrationsTree = (JTree) e.getComponent(); 17 | TreePath leadSelectionPath = migrationsTree.getLeadSelectionPath(); 18 | if (leadSelectionPath == null) { 19 | return; 20 | } 21 | 22 | DefaultMutableTreeNode object = (DefaultMutableTreeNode) leadSelectionPath.getLastPathComponent(); 23 | if (object.getUserObject() instanceof String || object.getUserObject() instanceof MigrateCommand) { 24 | if (migrationsTree.isExpanded(leadSelectionPath)) { 25 | migrationsTree.collapsePath(leadSelectionPath); 26 | } else { 27 | migrationsTree.expandPath(leadSelectionPath); 28 | } 29 | return; 30 | } 31 | 32 | Migration migration = (Migration) object.getUserObject(); 33 | if (migration.migrationClass.canNavigate()) { 34 | migration.migrationClass.navigate(true); 35 | } 36 | } 37 | 38 | } 39 | 40 | @Override 41 | public void mousePressed(MouseEvent e) { 42 | 43 | } 44 | 45 | @Override 46 | public void mouseReleased(MouseEvent e) { 47 | 48 | } 49 | 50 | @Override 51 | public void mouseEntered(MouseEvent e) { 52 | 53 | } 54 | 55 | @Override 56 | public void mouseExited(MouseEvent e) { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/migrations/util/MigrationUtil.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.migrations.util; 2 | 3 | import com.nvlad.yii2support.migrations.entities.Migration; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.text.ParseException; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | 12 | public class MigrationUtil { 13 | private static final SimpleDateFormat migrationApplyDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 14 | 15 | @Nullable 16 | public static Date parseApplyDate(String date) { 17 | try { 18 | return migrationApplyDateFormat.parse(date); 19 | } catch (ParseException e) { 20 | return null; 21 | } 22 | } 23 | 24 | public static List migrationPaths(List migrations) { 25 | List paths = new LinkedList<>(); 26 | for (Migration migration : migrations) { 27 | if (!paths.contains(migration.path)) { 28 | paths.add(migration.path); 29 | } 30 | } 31 | 32 | return paths; 33 | } 34 | 35 | public static List migrationNamespaces(List migrations) { 36 | List namespaces = new LinkedList<>(); 37 | for (Migration migration : migrations) { 38 | if (migration.namespace.equals("\\") && !namespaces.contains(migration.namespace)) { 39 | namespaces.add(migration.namespace); 40 | } 41 | } 42 | 43 | return namespaces; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/objectfactory/ObjectFactoryCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.objectfactory; 2 | 3 | import com.intellij.codeInsight.completion.AutoCompletionContext; 4 | import com.intellij.codeInsight.completion.AutoCompletionDecision; 5 | import com.intellij.codeInsight.completion.CompletionInitializationContext; 6 | import com.intellij.codeInsight.completion.CompletionType; 7 | import com.intellij.patterns.ElementPattern; 8 | import com.intellij.patterns.PlatformPatterns; 9 | import com.intellij.psi.PsiElement; 10 | import com.jetbrains.php.lang.psi.elements.ArrayAccessExpression; 11 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 12 | import com.jetbrains.php.lang.psi.elements.PhpPsiElement; 13 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 14 | import com.nvlad.yii2support.common.Patterns; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | public class ObjectFactoryCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { 19 | public ObjectFactoryCompletionContributor() { 20 | extend(CompletionType.BASIC, ElementPattern(), new ObjectFactoryCompletionProvider()); 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public AutoCompletionDecision handleAutoCompletionPossibility(@NotNull AutoCompletionContext context) { 26 | return super.handleAutoCompletionPossibility(context); 27 | } 28 | 29 | @Override 30 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 31 | if ((typeChar == '\'' || typeChar == '"') && 32 | (position.getParent() instanceof ArrayCreationExpression || position.getParent() instanceof ArrayAccessExpression)) { 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | private static ElementPattern ElementPattern() { 40 | return PlatformPatterns.psiElement() 41 | .withParent(PlatformPatterns.psiElement(StringLiteralExpression.class) 42 | .withParent(PlatformPatterns.or( 43 | PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class), 44 | PlatformPatterns.psiElement().withParent(ArrayAccessExpression.class), 45 | PlatformPatterns.psiElement(PhpPsiElement.class), 46 | Patterns.withHashKey().withParent(PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class)) 47 | ) 48 | ) 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/objectfactory/ObjectFactoryMissedFieldInspection.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.objectfactory; 2 | 3 | import com.intellij.codeInspection.ProblemsHolder; 4 | import com.intellij.psi.PsiDirectory; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiElementVisitor; 7 | import com.jetbrains.php.lang.inspections.PhpInspection; 8 | import com.jetbrains.php.lang.psi.elements.*; 9 | import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor; 10 | import com.nvlad.yii2support.common.ClassUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * Created by oleg on 15.03.2017. 15 | */ 16 | public class ObjectFactoryMissedFieldInspection extends PhpInspection { 17 | @NotNull 18 | @Override 19 | public String getShortName() { 20 | return "MissedFieldInspection"; 21 | } 22 | 23 | @NotNull 24 | @Override 25 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder problemsHolder, boolean isOnTheFly) { 26 | return new PhpElementVisitor() { 27 | @Override 28 | public void visitPhpArrayCreationExpression(ArrayCreationExpression expression) { 29 | PsiDirectory dir = expression.getContainingFile().getContainingDirectory(); 30 | PhpClass phpClass = ObjectFactoryUtils.findClassByArrayCreation(expression, dir); 31 | if (phpClass != null && !phpClass.getFQN().equals("\\" + phpClass.getName())) { // Avoid System Classes: \Closure, \ArrayAccess 32 | for (ArrayHashElement elem: expression.getHashElements()) { 33 | PsiElement key = elem.getKey(); 34 | if (key != null) { 35 | String keyName = (key instanceof ClassConstantReference || key instanceof ConstantReference) ? 36 | ClassUtils.getConstantValue(key) : key.getText(); 37 | if (keyName != null) { 38 | keyName = ClassUtils.removeQuotes(keyName); 39 | if (!keyName.equals("__class") 40 | && !keyName.equals("class") 41 | && !keyName.startsWith("as ") 42 | && !keyName.startsWith("on ") 43 | && ClassUtils.findWritableField(phpClass, keyName) == null) { 44 | final String descriptionTemplate = "Field '" + keyName + "' not exists in referenced class " + phpClass.getFQN(); 45 | problemsHolder.registerProblem(elem, descriptionTemplate); 46 | } 47 | } 48 | } 49 | } 50 | 51 | 52 | } 53 | super.visitPhpArrayCreationExpression(expression); 54 | } 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/objectfactory/ObjectFactoryReference.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.objectfactory; 2 | 3 | import com.intellij.psi.PsiDirectory; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiReferenceBase; 6 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 7 | import com.jetbrains.php.lang.psi.elements.PhpClass; 8 | import com.nvlad.yii2support.common.ClassUtils; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * Created by oleg on 14.03.2017. 14 | */ 15 | public class ObjectFactoryReference extends PsiReferenceBase { 16 | ObjectFactoryReference(@NotNull PsiElement element) 17 | { 18 | super(element, element.getTextRange()); 19 | } 20 | 21 | @Nullable 22 | @Override 23 | public PsiElement resolve() { 24 | PsiElement possibleArrayCreation = myElement.getParent().getParent().getParent(); 25 | if (possibleArrayCreation instanceof ArrayCreationExpression) { 26 | ArrayCreationExpression arrayCreation = (ArrayCreationExpression)possibleArrayCreation; 27 | PsiDirectory dir = myElement.getContainingFile().getContainingDirectory(); 28 | PhpClass phpClass = ObjectFactoryUtils.findClassByArrayCreation(arrayCreation, dir); 29 | 30 | if (phpClass != null) { 31 | return ClassUtils.findWritableField(phpClass, myElement.getText()); 32 | } 33 | 34 | } 35 | return null; 36 | } 37 | 38 | @NotNull 39 | @Override 40 | public Object[] getVariants() { 41 | return new Object[0]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/objectfactory/ObjectFactoryReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.objectfactory; 2 | 3 | import com.intellij.patterns.ElementPattern; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiReferenceRegistrar; 7 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 8 | import com.nvlad.yii2support.common.Patterns; 9 | 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class ObjectFactoryReferenceContributor extends com.intellij.psi.PsiReferenceContributor { 13 | @Override 14 | public void registerReferenceProviders(@NotNull PsiReferenceRegistrar psiReferenceRegistrar) { 15 | psiReferenceRegistrar.registerReferenceProvider(ElementPattern(), new ObjectFactoryReferenceProvider()); 16 | } 17 | 18 | private static ElementPattern ElementPattern() { 19 | return PlatformPatterns.psiElement() 20 | .withParent(PlatformPatterns.or( 21 | PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class), 22 | Patterns.withHashKey() 23 | .withParent(PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class)) 24 | )); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/objectfactory/ObjectFactoryReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.objectfactory; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.util.ProcessingContext; 5 | 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by oleg on 14.03.2017. 13 | */ 14 | public class ObjectFactoryReferenceProvider extends com.intellij.psi.PsiReferenceProvider { 15 | @NotNull 16 | @Override 17 | public ObjectFactoryReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) { 18 | List references = new ArrayList<>(); 19 | 20 | ObjectFactoryReference reference = new ObjectFactoryReference(psiElement); 21 | references.add(reference); 22 | 23 | 24 | return references.toArray(new ObjectFactoryReference[0]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/objectfactory/README.md: -------------------------------------------------------------------------------- 1 | Code completion of keys in arrays that have "class" key and valid class reference in value. 2 | Following class reference representations supported: 3 | - String represention, 4 | - Class::class 5 | - Class::className(). 6 | 7 | Code completion also works for standard classes (detected by key) in config directory. 8 | 9 | widget() and begin() method for \yii\base\Widget class descendants is supported. 10 | 11 | widget() method for \yii\widgets\ActiveField is supported 12 | 13 | Code completion for GridView columns is supported 14 | 15 | This module supports Go To Declaration, Rename, Find usages 16 | 17 | ------------------------------------ 18 | Configuration arrays 19 | 20 | Code completion for Yii configuration arrays. Works both in configuration files and on object instantiation. 21 | Following cases are supported: 22 | * Array in $config parameter in yii\base\Object or its descendants constructor 23 | * Array have "class" key with valid class representation: fully qualified string representation, ClassName::class or Class::className() 24 | * Array is a value of a key that corresponds to standard Yii classes (like "db", "request", "mailer" and so on), and file with this array located in a directory called "config" 25 | * WidgetClass::widget() and WidgetClass::begin calls if WidgetClass is a descendant of yii\base\Widget 26 | * $field->widget() method call on yii\widgets\ActiveField and its descendants 27 | * Inside array in GridView "columns" key 28 | 29 | 30 | Go To Declaration, Rename, Find usages and Help popup works whenever code completion works -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/properties/MissingPropertiesQuickFix.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.properties; 2 | 3 | import com.intellij.codeInsight.template.Template; 4 | import com.intellij.codeInsight.template.TemplateManager; 5 | import com.intellij.codeInspection.LocalQuickFix; 6 | import com.intellij.codeInspection.ProblemDescriptor; 7 | import com.intellij.openapi.editor.Document; 8 | import com.intellij.openapi.editor.Editor; 9 | import com.intellij.openapi.fileEditor.FileEditorManager; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.psi.PsiDocumentManager; 12 | import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment; 13 | import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocPropertyTag; 14 | import com.nvlad.yii2support.common.VirtualProperty; 15 | import org.jetbrains.annotations.Nls; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * Created by oleg on 06.04.2017. 23 | */ 24 | public class MissingPropertiesQuickFix implements LocalQuickFix { 25 | 26 | private ArrayList missingProperties; 27 | private PhpDocComment comment; 28 | 29 | public MissingPropertiesQuickFix(ArrayList missingProperties, PhpDocComment comment) { 30 | this.missingProperties = missingProperties; 31 | this.comment = comment; 32 | } 33 | 34 | @Nls 35 | @NotNull 36 | @Override 37 | public String getName() { 38 | return getFamilyName(); 39 | } 40 | 41 | @Nls 42 | @NotNull 43 | @Override 44 | public String getFamilyName() { 45 | return "Fix missing properties"; 46 | } 47 | 48 | @Override 49 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor problemDescriptor) { 50 | List propertyTags = this.comment.getPropertyTags(); 51 | 52 | Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); 53 | if (editor == null) 54 | return; 55 | Document document = editor.getDocument(); 56 | PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); 57 | TemplateManager templateManager = TemplateManager.getInstance(project); 58 | Template template = templateManager.createTemplate("", ""); 59 | template.setToReformat(true); 60 | for (VirtualProperty missingProperty : this.missingProperties) { 61 | 62 | String propertyText = "* @property " + (missingProperty.getType() != null ? missingProperty.getType() : "") + " $" + missingProperty.getName(); 63 | if (missingProperty.getComment() != null) { 64 | propertyText += " " + missingProperty.getComment(); 65 | } 66 | template.addTextSegment("\n" + propertyText); 67 | } 68 | template.addTextSegment("\n"); 69 | 70 | int offset = comment.getLastChild().getTextOffset(); 71 | if (propertyTags.size() > 0) { 72 | PhpDocPropertyTag phpDocPropertyTag = propertyTags.get(comment.getPropertyTags().size() - 1); 73 | offset = phpDocPropertyTag.getTextOffset() + phpDocPropertyTag.getTextLength(); 74 | } 75 | editor.getCaretModel().moveToOffset(offset); 76 | PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); 77 | templateManager.startTemplate(editor, template); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/properties/PropertiesInspection.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.properties; 2 | 3 | import com.intellij.codeInspection.ProblemHighlightType; 4 | import com.intellij.codeInspection.ProblemsHolder; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiElementVisitor; 7 | import com.jetbrains.php.PhpIndex; 8 | import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment; 9 | import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocPropertyTag; 10 | import com.jetbrains.php.lang.inspections.PhpInspection; 11 | import com.jetbrains.php.lang.psi.elements.*; 12 | import com.jetbrains.php.lang.psi.visitors.PhpElementVisitor; 13 | import com.nvlad.yii2support.common.ClassUtils; 14 | import com.nvlad.yii2support.common.DatabaseUtils; 15 | import com.nvlad.yii2support.common.VirtualProperty; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | 21 | /** 22 | * Created by oleg on 06.04.2017. 23 | */ 24 | public class PropertiesInspection extends PhpInspection { 25 | 26 | 27 | @NotNull 28 | @Override 29 | public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder problemsHolder, boolean isOnTheFly) { 30 | 31 | 32 | return new PhpElementVisitor() { 33 | 34 | @Override 35 | public void visitElement(PsiElement element) { 36 | if (element instanceof PhpDocComment && DatabaseUtils.HasConnections(element.getProject())) { 37 | PhpDocComment docComment = (PhpDocComment) element; 38 | PhpIndex index = PhpIndex.getInstance(element.getProject()); 39 | 40 | PhpClass phpClass = DatabaseUtils.getClassByClassPhpDoc(docComment); 41 | if (phpClass != null && ClassUtils.isClassInheritsOrEqual(phpClass, ClassUtils.getClass(index, "\\yii\\db\\BaseActiveRecord"), 100)) { 42 | Collection fields = phpClass.getFields(); 43 | String table = DatabaseUtils.getTableByActiveRecordClass(phpClass); 44 | ArrayList notDeclaredColumns = DatabaseUtils.getNotDeclaredColumns(table, fields, element.getProject()); 45 | if (notDeclaredColumns.size() > 0) { 46 | MissingPropertiesQuickFix qFix = new MissingPropertiesQuickFix(notDeclaredColumns, docComment); 47 | String str1 = notDeclaredColumns.size() > 1 ? "properties" : "property"; 48 | problemsHolder.registerProblem(docComment, "Class " + phpClass.getFQN() + 49 | " is missing " + notDeclaredColumns.size() + " " + str1 + " that corresponds to database columns", ProblemHighlightType.WEAK_WARNING, qFix); 50 | } 51 | 52 | ArrayList unusedProperties = DatabaseUtils.getUnusedProperties(table, docComment.getPropertyTags(), phpClass); 53 | if (unusedProperties.size() > 0) { 54 | for (PhpDocPropertyTag tag: unusedProperties) { 55 | problemsHolder.registerProblem(tag, "Property is unused in class " + phpClass.getFQN(), ProblemHighlightType.LIKE_UNUSED_SYMBOL); 56 | } 57 | } 58 | } 59 | 60 | } 61 | super.visitElement(element); 62 | } 63 | 64 | 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/properties/TODO.md: -------------------------------------------------------------------------------- 1 | **Properties not generated for relations** 2 | ```php 3 | public function getSpec() { 4 | return $this->hasMany(DoctorSpec::className(), ['id' => 'doctorspec_id'])->andOnCondition(['address'] ) 5 | ->viaTable("doctor_doctorspec", ['doctor_id' => 'id']); 6 | } 7 | ``` 8 | ** Generate properties for getters and setters** 9 | 10 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/typeprovider/ActiveRecordTypeProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.typeprovider; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.php.PhpIndex; 6 | import com.jetbrains.php.lang.psi.elements.*; 7 | import com.jetbrains.php.lang.psi.resolve.types.PhpType; 8 | import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4; 9 | import com.nvlad.yii2support.common.ClassUtils; 10 | import com.nvlad.yii2support.common.SignatureUtils; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.Collection; 14 | import java.util.Set; 15 | 16 | /** 17 | * Created by oleg on 2017-06-24. 18 | */ 19 | public class ActiveRecordTypeProvider implements PhpTypeProvider4 { 20 | final static char TRIM_KEY = '\u0197'; 21 | 22 | @Override 23 | public char getKey() { 24 | return '\u0856'; 25 | } 26 | 27 | @Nullable 28 | @Override 29 | public PhpType getType(PsiElement psiElement) { 30 | if (psiElement instanceof MethodReference) { 31 | MethodReference methodReference = (MethodReference)psiElement; 32 | String methodName = methodReference.getName(); 33 | if (methodName == null) 34 | return null; 35 | if (methodName.equals("one") || methodName.equals("all") 36 | || methodName.equals("each") || methodName.equals("batch")) { 37 | String signature = methodReference.getSignature(); 38 | int beginIndex = signature.indexOf("\\"); 39 | int endIndex = signature.indexOf("|"); 40 | if (endIndex < 0) { 41 | endIndex = signature.length(); 42 | } 43 | if (beginIndex > -1 && beginIndex < endIndex) { 44 | signature = signature.substring(beginIndex, endIndex); 45 | return new PhpType().add("#" + this.getKey() + signature); 46 | } 47 | } 48 | } 49 | return null; 50 | } 51 | 52 | @Override 53 | @Nullable 54 | public PhpType complete(String s, Project project) { 55 | PhpType phpType = new PhpType(); 56 | 57 | int endIndex = s.lastIndexOf(this.getKey()); 58 | if(endIndex == -1) { 59 | return null; 60 | } 61 | 62 | PhpClass classBySignature = SignatureUtils.getClassBySignature(s, project); 63 | boolean classInheritsFromAD = ClassUtils.isClassInherit(classBySignature, "\\yii\\db\\BaseActiveRecord", PhpIndex.getInstance(project)); 64 | if (classInheritsFromAD) { 65 | if (s.endsWith(".one")) 66 | phpType.add(classBySignature.getFQN()); 67 | else if (s.endsWith(".all") || s.endsWith(".each")) { 68 | phpType.add(classBySignature.getFQN()+ "[]"); 69 | }else if (s.endsWith(".batch")) { 70 | phpType.add(classBySignature.getFQN()+ "[][]"); 71 | } 72 | } 73 | return phpType; 74 | } 75 | 76 | @Override 77 | public Collection getBySignature(String s, Set set, int i, Project project) { 78 | return null; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/ui/settings/SettingsForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/ui/settings/SettingsForm.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.ui.settings; 2 | 3 | import com.intellij.openapi.fileChooser.FileChooserDescriptor; 4 | import com.intellij.openapi.options.Configurable; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.TextBrowseFolderListener; 7 | import com.intellij.openapi.ui.TextFieldWithBrowseButton; 8 | import com.intellij.openapi.util.text.StringUtil; 9 | import com.intellij.util.indexing.FileBasedIndex; 10 | import com.nvlad.yii2support.common.YiiApplicationUtils; 11 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 12 | import com.nvlad.yii2support.views.index.ViewFileIndex; 13 | import org.jetbrains.annotations.Nls; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | import javax.swing.*; 17 | 18 | public class SettingsForm implements Configurable { 19 | private JPanel mainPanel; 20 | private TextFieldWithBrowseButton yiiRootPath; 21 | private JLabel yiiRootPathLabel; 22 | final private Project myProject; 23 | final private Yii2SupportSettings settings; 24 | 25 | public SettingsForm(Project project) { 26 | myProject = project; 27 | settings = Yii2SupportSettings.getInstance(project); 28 | 29 | yiiRootPathLabel.setLabelFor(yiiRootPath.getTextField()); 30 | yiiRootPath.setButtonEnabled(true); 31 | FileChooserDescriptor fileChooserDescriptor = new FileChooserDescriptor(false, true, false, false, false, false); 32 | yiiRootPath.addBrowseFolderListener(new TextBrowseFolderListener(fileChooserDescriptor)); 33 | } 34 | 35 | @Nls 36 | @Override 37 | public String getDisplayName() { 38 | return "Yii2 Support"; 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public String getHelpTopic() { 44 | return null; 45 | } 46 | 47 | @Nullable 48 | @Override 49 | public JComponent createComponent() { 50 | return mainPanel; 51 | } 52 | 53 | @Override 54 | public boolean isModified() { 55 | return !yiiRootPath.getText().trim().equals(StringUtil.notNullize(settings.yiiRootPath)); 56 | } 57 | 58 | @Override 59 | public void apply() { 60 | settings.yiiRootPath = StringUtil.nullize(yiiRootPath.getText().trim()); 61 | 62 | YiiApplicationUtils.resetYiiRootPath(myProject); 63 | FileBasedIndex.getInstance().requestRebuild(ViewFileIndex.identity); 64 | } 65 | 66 | @Override 67 | public void reset() { 68 | yiiRootPath.setText(StringUtil.notNullize(settings.yiiRootPath)); 69 | } 70 | 71 | @Override 72 | public void disposeUIResources() { 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/validation/RulePositionEnum.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.validation; 2 | 3 | /** 4 | * Created by oleg on 21.04.2017. 5 | */ 6 | public enum RulePositionEnum { 7 | UNKNOWN, FIELD, TYPE, OPTIONS 8 | } 9 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/validation/ValidationCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.validation; 2 | 3 | import com.intellij.codeInsight.completion.CompletionType; 4 | import com.intellij.patterns.ElementPattern; 5 | import com.intellij.patterns.PlatformPatterns; 6 | import com.intellij.psi.PsiElement; 7 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 8 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 9 | import com.nvlad.yii2support.common.Patterns; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | /** 13 | * Created by oleg on 20.04.2017. 14 | */ 15 | public class ValidationCompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { 16 | public ValidationCompletionContributor() { 17 | extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new ValidationCompletionProvider()); 18 | } 19 | 20 | @Override 21 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 22 | if ((typeChar == '\'' || typeChar == '"') && position.getParent() instanceof ArrayCreationExpression) { 23 | return true; 24 | } 25 | 26 | return false; 27 | } 28 | 29 | private static ElementPattern ElementPattern() { 30 | 31 | return PlatformPatterns.psiElement() 32 | .withParent(PlatformPatterns.psiElement(StringLiteralExpression.class) 33 | .withParent(PlatformPatterns.or( 34 | PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class), 35 | Patterns.withHashKey() 36 | .withParent(PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class)) 37 | ))); 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/validation/entities/Validator.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.validation.entities; 2 | 3 | import com.jetbrains.php.lang.psi.elements.PhpNamedElement; 4 | 5 | public class Validator { 6 | public String alias; 7 | 8 | public PhpNamedElement validator; 9 | 10 | public Validator(PhpNamedElement element) { 11 | alias = null; 12 | validator = element; 13 | } 14 | 15 | public Validator(String alias, PhpNamedElement element) { 16 | this.alias = alias; 17 | validator = element; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/actions/ReferenceListPopupStep.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.actions; 2 | 3 | import com.intellij.openapi.editor.Document; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.ui.popup.PopupStep; 6 | import com.intellij.openapi.ui.popup.util.BaseListPopupStep; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import com.intellij.pom.Navigatable; 9 | import com.intellij.psi.PsiDocumentManager; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiReference; 12 | import com.intellij.psi.util.PsiTreeUtil; 13 | import com.jetbrains.php.PhpIcons; 14 | import com.jetbrains.php.lang.psi.elements.MethodReference; 15 | import com.nvlad.yii2support.common.FileUtil; 16 | import org.jetbrains.annotations.NotNull; 17 | import org.jetbrains.annotations.Nullable; 18 | 19 | import javax.swing.*; 20 | import java.util.Collection; 21 | import java.util.LinkedList; 22 | 23 | class ReferenceListPopupStep extends BaseListPopupStep { 24 | ReferenceListPopupStep(@Nullable String title, Collection values) { 25 | super(title, new LinkedList<>(values)); 26 | } 27 | 28 | @Override 29 | public PopupStep onChosen(PsiReference reference, boolean finalChoice) { 30 | openReference(reference); 31 | return FINAL_CHOICE; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public String getTextFor(PsiReference reference) { 37 | if (reference == null) { 38 | return "(empty)"; 39 | } 40 | 41 | PsiElement psiElement = reference.getElement(); 42 | if (psiElement == null) { 43 | return "(empty)"; 44 | } 45 | 46 | PsiElement methodElement = PsiTreeUtil.getParentOfType(psiElement, MethodReference.class); 47 | if (methodElement == null) { 48 | return "(empty)"; 49 | } 50 | 51 | Project project = methodElement.getProject(); 52 | VirtualFile virtualFile = FileUtil.getVirtualFile(methodElement.getContainingFile()); 53 | String fileName = virtualFile.getUrl().replace(project.getBaseDir().getUrl(), ""); 54 | 55 | Document document = PsiDocumentManager.getInstance(project).getDocument(methodElement.getContainingFile()); 56 | if (document != null) { 57 | fileName += ":" + (document.getLineNumber(psiElement.getTextOffset()) + 1); 58 | } 59 | 60 | return methodElement.getText() + " [..." + fileName + "]"; 61 | } 62 | 63 | @Override 64 | public Icon getIconFor(PsiReference reference) { 65 | return PhpIcons.METHOD_ICON; 66 | } 67 | 68 | 69 | static void openReference(PsiReference reference) { 70 | PsiElement psiElement = reference.getElement(); 71 | 72 | if (psiElement.getFirstChild() != null && psiElement.getFirstChild().getNextSibling() != null) { 73 | psiElement = psiElement.getFirstChild().getNextSibling(); 74 | } 75 | 76 | if (psiElement instanceof Navigatable && ((Navigatable) psiElement).canNavigate()) { 77 | ((Navigatable) psiElement).navigate(true); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/completion/CompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionType; 4 | import com.intellij.patterns.ElementPattern; 5 | import com.intellij.patterns.PlatformPatterns; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 8 | import com.intellij.psi.util.PsiTreeUtil; 9 | import com.intellij.util.ArrayUtil; 10 | import com.jetbrains.php.lang.psi.elements.MethodReference; 11 | import com.jetbrains.php.lang.psi.elements.ParameterList; 12 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 13 | import com.nvlad.yii2support.common.Patterns; 14 | import com.nvlad.yii2support.views.util.ViewUtil; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | /** 18 | * Created by NVlad on 27.12.2016. 19 | */ 20 | public class CompletionContributor extends com.intellij.codeInsight.completion.CompletionContributor { 21 | public CompletionContributor() { 22 | extend(CompletionType.BASIC, ElementPattern(), new CompletionProvider()); 23 | } 24 | 25 | @Override 26 | public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { 27 | MethodReference reference = PsiTreeUtil.getParentOfType(position, MethodReference.class); 28 | if (reference != null && ArrayUtil.contains(reference.getName(), ViewUtil.renderMethods)) { 29 | if (typeChar == '\'' || typeChar == '"') { 30 | if (position instanceof LeafPsiElement && position.getText().equals("$view")) { 31 | return true; 32 | } 33 | if (position.getNextSibling() instanceof ParameterList) { 34 | return true; 35 | } 36 | } 37 | if (typeChar == '@' && position.getParent() instanceof StringLiteralExpression) { 38 | ParameterList parameterList = PsiTreeUtil.getParentOfType(position, ParameterList.class); 39 | return parameterList != null && parameterList.getParameters()[0] == position.getParent(); 40 | } 41 | } 42 | 43 | return false; 44 | } 45 | 46 | private static ElementPattern ElementPattern() { 47 | return PlatformPatterns.psiElement() 48 | .withSuperParent(3, Patterns.methodWithName(ViewUtil.renderMethods)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/completion/ViewLookupElement.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.completion; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementPresentation; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import com.intellij.psi.PsiFile; 7 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * Created by NVlad on 28.12.2016. 12 | */ 13 | class ViewLookupElement extends LookupElement { 14 | final private PsiFile myFile; 15 | final private String myName; 16 | final private String myTail; 17 | 18 | ViewLookupElement(PsiFile psiFile, String insertText) { 19 | myFile = psiFile; 20 | VirtualFile file = psiFile.getVirtualFile(); 21 | 22 | myName = insertText; 23 | final String ext = file.getExtension(); 24 | final String defaultViewExtension = Yii2SupportSettings.getInstance(myFile.getProject()).defaultViewExtension; 25 | if (ext != null && ext.equals(defaultViewExtension) && !insertText.endsWith("." + defaultViewExtension)) { 26 | myTail = "." + file.getExtension(); 27 | } else { 28 | myTail = null; 29 | } 30 | } 31 | 32 | @NotNull 33 | @Override 34 | public String getLookupString() { 35 | return myName; 36 | } 37 | 38 | @Override 39 | public void renderElement(LookupElementPresentation presentation) { 40 | presentation.setIcon(myFile.getIcon(0)); 41 | presentation.setItemText(myName); 42 | presentation.setItemTextBold(true); 43 | if (myTail != null) { 44 | presentation.setTailText(myTail, true); 45 | } 46 | presentation.setTypeText("View"); 47 | presentation.setTypeGrayed(true); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/entities/ViewInfo.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.entities; 2 | 3 | import com.intellij.openapi.util.text.StringUtil; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.openapi.vfs.VirtualFileManager; 6 | import com.intellij.util.indexing.FileContent; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collection; 11 | 12 | public class ViewInfo { 13 | public String fileUrl; 14 | public String application; 15 | public String theme; 16 | public Collection parameters; 17 | private VirtualFile myVirtualFile; 18 | 19 | public ViewInfo() { 20 | 21 | } 22 | 23 | public ViewInfo(FileContent inputData) { 24 | myVirtualFile = inputData.getFile(); 25 | fileUrl = myVirtualFile.getUrl(); 26 | } 27 | 28 | @Nullable 29 | public VirtualFile getVirtualFile() { 30 | if (myVirtualFile == null) { 31 | myVirtualFile = VirtualFileManager.getInstance().findFileByUrl(fileUrl); 32 | } 33 | 34 | return myVirtualFile; 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return fileUrl.hashCode() + application.hashCode() + theme.hashCode() + parameters.hashCode(); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (this == obj) { 45 | return true; 46 | } 47 | 48 | 49 | if (!(obj instanceof ViewInfo)) { 50 | return false; 51 | } 52 | 53 | ViewInfo viewInfo = (ViewInfo) obj; 54 | return StringUtil.equals(this.fileUrl, viewInfo.fileUrl) 55 | && StringUtil.equals(this.application, viewInfo.application) 56 | && StringUtil.equals(this.theme, viewInfo.theme) 57 | && Arrays.equals(new Collection[]{this.parameters}, new Collection[]{viewInfo.parameters}); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/entities/ViewResolve.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.entities; 2 | 3 | public class ViewResolve { 4 | public String key; 5 | public String application; 6 | public String module; 7 | public String theme; 8 | public ViewResolveFrom from; 9 | public String relativePath; 10 | 11 | public ViewResolve() { 12 | } 13 | 14 | public ViewResolve(String key) { 15 | this.key = key; 16 | } 17 | 18 | public ViewResolve(ViewResolveFrom from) { 19 | this.from = from; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/entities/ViewResolveFrom.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.entities; 2 | 3 | public enum ViewResolveFrom { 4 | Controller, 5 | View, 6 | Widget, 7 | } 8 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/inspections/UnusedParameterLocalQuickFix.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.inspections; 2 | 3 | import com.intellij.codeInspection.LocalQuickFix; 4 | import com.intellij.codeInspection.ProblemDescriptor; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiWhiteSpace; 8 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 9 | import com.jetbrains.php.lang.psi.elements.FunctionReference; 10 | import com.jetbrains.php.lang.psi.elements.ParameterList; 11 | import com.nvlad.yii2support.common.PsiUtil; 12 | import org.jetbrains.annotations.Nls; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | /** 16 | * Created by NVlad on 16.01.2017. 17 | */ 18 | class UnusedParameterLocalQuickFix implements LocalQuickFix { 19 | final private String myParam; 20 | 21 | UnusedParameterLocalQuickFix(String param) { 22 | myParam = param; 23 | } 24 | 25 | @Nls 26 | @NotNull 27 | @Override 28 | public String getName() { 29 | return "Remove unused parameter \"%param%\"".replace("%param%", myParam); 30 | } 31 | 32 | @Nls 33 | @NotNull 34 | @Override 35 | public String getFamilyName() { 36 | return "Remove unused parameter"; 37 | } 38 | 39 | @Override 40 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { 41 | PsiElement item = descriptor.getPsiElement(); 42 | 43 | PsiElement context = item.getContext(); 44 | if (context instanceof ArrayCreationExpression) { 45 | ArrayCreationExpression params = (ArrayCreationExpression) item.getParent(); 46 | 47 | PsiUtil.deleteArrayElement(item); 48 | 49 | if (!params.getHashElements().iterator().hasNext()) { 50 | if (params.getPrevSibling() instanceof PsiWhiteSpace) { 51 | params.getPrevSibling().delete(); 52 | } 53 | params.getPrevSibling().delete(); 54 | params.delete(); 55 | } 56 | } 57 | if (context instanceof ParameterList && context.getParent() instanceof FunctionReference) { 58 | FunctionReference functionReference = (FunctionReference) context.getParent(); 59 | if (functionReference.getName() != null && functionReference.getName().equals("compact")) { 60 | PsiUtil.deleteFunctionParam(item); 61 | 62 | if (functionReference.getParameters().length == 0) { 63 | PsiUtil.deleteFunctionParam(functionReference); 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/inspections/UnusedParametersLocalQuickFix.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.inspections; 2 | 3 | import com.intellij.codeInspection.LocalQuickFix; 4 | import com.intellij.codeInspection.ProblemDescriptor; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiWhiteSpace; 8 | import com.jetbrains.php.lang.psi.elements.*; 9 | import com.nvlad.yii2support.common.PsiUtil; 10 | import org.jetbrains.annotations.Nls; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Collection; 14 | import java.util.HashSet; 15 | import java.util.Set; 16 | 17 | /** 18 | * Created by NVlad on 16.01.2017. 19 | */ 20 | class UnusedParametersLocalQuickFix implements LocalQuickFix { 21 | private Collection myUnusedParams; 22 | 23 | UnusedParametersLocalQuickFix() { 24 | } 25 | 26 | UnusedParametersLocalQuickFix(Collection unusedParams) { 27 | myUnusedParams = unusedParams; 28 | } 29 | 30 | @Nls 31 | @NotNull 32 | @Override 33 | public String getName() { 34 | return getFamilyName(); 35 | } 36 | 37 | @Nls 38 | @NotNull 39 | @Override 40 | public String getFamilyName() { 41 | return "Remove all unused parameters"; 42 | } 43 | 44 | @Override 45 | public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { 46 | PsiElement viewParameters = descriptor.getPsiElement(); 47 | if (viewParameters instanceof MethodReference) { 48 | viewParameters = ((MethodReference) viewParameters).getParameters()[1]; 49 | } 50 | 51 | if (myUnusedParams != null) { 52 | final Set unused = new HashSet<>(); 53 | if (viewParameters instanceof ArrayCreationExpression) { 54 | final ArrayCreationExpression params = (ArrayCreationExpression) viewParameters; 55 | for (ArrayHashElement element : params.getHashElements()) { 56 | if (element.getKey() != null) { 57 | final String key = ((StringLiteralExpression) element.getKey()).getContents(); 58 | if (myUnusedParams.contains(key)) { 59 | unused.add(element); 60 | } 61 | } 62 | } 63 | for (PsiElement element : unused) { 64 | PsiUtil.deleteArrayElement(element); 65 | } 66 | 67 | if (!params.getHashElements().iterator().hasNext()) { 68 | if (params.getPrevSibling() instanceof PsiWhiteSpace) { 69 | params.getPrevSibling().delete(); 70 | } 71 | params.getPrevSibling().delete(); 72 | params.delete(); 73 | } 74 | } 75 | if (viewParameters instanceof FunctionReference) { 76 | for (PsiElement element : ((FunctionReference) viewParameters).getParameters()) { 77 | if (element instanceof StringLiteralExpression) { 78 | if (myUnusedParams.contains(((StringLiteralExpression) element).getContents())) { 79 | unused.add(element); 80 | } 81 | } 82 | } 83 | for (PsiElement element : unused) { 84 | PsiUtil.deleteFunctionParam(element); 85 | } 86 | } 87 | } else { 88 | if (viewParameters.getPrevSibling() instanceof PsiWhiteSpace) { 89 | viewParameters.getPrevSibling().delete(); 90 | } 91 | viewParameters.getPrevSibling().delete(); 92 | viewParameters.delete(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/inspections/ViewMissedPhpDocLocalQuickFix.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.inspections; 2 | 3 | import com.intellij.codeInspection.LocalQuickFixOnPsiElement; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.fileEditor.FileEditorManager; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.psi.PsiFile; 9 | import com.intellij.psi.util.PsiTreeUtil; 10 | import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment; 11 | import com.jetbrains.php.lang.psi.elements.GroupStatement; 12 | import org.jetbrains.annotations.Nls; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Collection; 16 | import java.util.Iterator; 17 | import java.util.Map; 18 | 19 | public class ViewMissedPhpDocLocalQuickFix extends LocalQuickFixOnPsiElement { 20 | private final Map myVariables; 21 | 22 | ViewMissedPhpDocLocalQuickFix(PsiElement element, Map variables) { 23 | super(element); 24 | myVariables = variables; 25 | } 26 | 27 | @NotNull 28 | @Override 29 | public String getText() { 30 | return getFamilyName(); 31 | } 32 | 33 | @Nls 34 | @NotNull 35 | @Override 36 | public String getFamilyName() { 37 | return "Add missed variable declarations"; 38 | } 39 | 40 | @Override 41 | public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { 42 | Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); 43 | if (editor == null) { 44 | return; 45 | } 46 | 47 | StringBuilder stringBuilder = new StringBuilder(); 48 | for (String key : myVariables.keySet()) { 49 | stringBuilder.append("\n/* @var $"); 50 | stringBuilder.append(key); 51 | stringBuilder.append(" "); 52 | stringBuilder.append(myVariables.get(key)); 53 | stringBuilder.append(" */"); 54 | } 55 | 56 | int insertPosition = getInsertPosition(psiFile); 57 | if (insertPosition == 0) { 58 | stringBuilder.insert(0, " 0) { 61 | stringBuilder.append("\n?>\n"); 62 | } 63 | } 64 | if (insertPosition == 5) { 65 | stringBuilder.insert(0, "\n"); 66 | } 67 | 68 | editor.getDocument().insertString(insertPosition, stringBuilder); 69 | } 70 | 71 | @Override 72 | public boolean startInWriteAction() { 73 | return true; 74 | } 75 | 76 | private int getInsertPosition(PsiFile psiFile) { 77 | if (!(psiFile.getFirstChild() instanceof GroupStatement)) { 78 | return 0; 79 | } 80 | 81 | PsiElement element = psiFile; 82 | if (psiFile.getChildren().length == 1) { 83 | element = element.getFirstChild(); 84 | } 85 | if (element.getFirstChild().getText().equals("")) { 92 | break; 93 | } 94 | 95 | if (psiElement instanceof PhpDocComment) { 96 | phpDocComment = psiElement; 97 | } 98 | } 99 | if (phpDocComment != null) { 100 | return phpDocComment.getTextRange().getEndOffset(); 101 | } 102 | 103 | return 5; 104 | } 105 | 106 | return 0; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/navigation/ViewGotoDeclarationHandler.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.navigation; 2 | 3 | import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.util.io.FileUtilRt; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.psi.PsiFile; 9 | import com.intellij.psi.PsiManager; 10 | import com.intellij.psi.search.GlobalSearchScope; 11 | import com.intellij.util.indexing.FileBasedIndex; 12 | import com.nvlad.yii2support.common.PhpUtil; 13 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 14 | import com.nvlad.yii2support.views.entities.ViewInfo; 15 | import com.nvlad.yii2support.views.entities.ViewResolve; 16 | import com.nvlad.yii2support.views.entities.ViewResolveFrom; 17 | import com.nvlad.yii2support.views.index.ViewFileIndex; 18 | import com.nvlad.yii2support.views.util.ViewUtil; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.util.Collection; 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | public class ViewGotoDeclarationHandler implements GotoDeclarationHandler { 26 | @Override 27 | public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) { 28 | if (psiElement == null) { 29 | return new PsiElement[0]; 30 | } 31 | 32 | Set result = new HashSet<>(); 33 | 34 | final ViewResolve resolve = ViewUtil.resolveView(psiElement); 35 | if (resolve != null) { 36 | Project project = psiElement.getProject(); 37 | 38 | String key = resolve.key; 39 | if (FileUtilRt.getExtension(key).isEmpty()) { 40 | key = key + '.' + Yii2SupportSettings.getInstance(psiElement.getProject()).defaultViewExtension; 41 | } 42 | 43 | final Collection views = FileBasedIndex.getInstance() 44 | .getValues(ViewFileIndex.identity, key, GlobalSearchScope.projectScope(project)); 45 | 46 | if (views.size() > 0) { 47 | boolean localViewSearch = false; 48 | if (resolve.from == ViewResolveFrom.View) { 49 | final String value = PhpUtil.getValue(psiElement); 50 | localViewSearch = !value.startsWith("@") && !value.startsWith("//"); 51 | } 52 | for (ViewInfo view : views) { 53 | if (!resolve.application.equals(view.application)) { 54 | continue; 55 | } 56 | 57 | if (localViewSearch && !resolve.theme.equals(view.theme)) { 58 | continue; 59 | } 60 | 61 | if (view.getVirtualFile() == null) { 62 | continue; 63 | } 64 | 65 | PsiFile file = PsiManager.getInstance(project).findFile(view.getVirtualFile()); 66 | result.add(file); 67 | } 68 | } 69 | } 70 | 71 | return result.toArray(new PsiElement[0]); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/refactor/RenameViewProcessor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.refactor; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiReference; 5 | import com.intellij.refactoring.RefactoringSettings; 6 | import com.intellij.refactoring.listeners.RefactoringElementListener; 7 | import com.intellij.refactoring.rename.RenamePsiFileProcessor; 8 | import com.intellij.util.indexing.FileBasedIndex; 9 | import com.jetbrains.php.lang.psi.PhpFile; 10 | import com.jetbrains.php.lang.psi.PhpPsiElementFactory; 11 | import com.jetbrains.php.lang.psi.elements.ParameterList; 12 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 13 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 14 | import com.nvlad.yii2support.views.index.ViewFileIndex; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.util.HashSet; 19 | import java.util.Map; 20 | import java.util.Set; 21 | 22 | /** 23 | * Created by NVlad on 02.02.2017. 24 | */ 25 | public class RenameViewProcessor extends RenamePsiFileProcessor { 26 | private final Set renders = new HashSet<>(); 27 | 28 | @Override 29 | public boolean canProcessElement(@NotNull PsiElement psiElement) { 30 | return psiElement instanceof PhpFile; 31 | } 32 | 33 | @Override 34 | public void prepareRenaming(PsiElement psiElement, String s, Map map) { 35 | renders.clear(); 36 | 37 | if (!RefactoringSettings.getInstance().RENAME_SEARCH_FOR_REFERENCES_FOR_FILE) { 38 | return; 39 | } 40 | 41 | for (PsiReference reference : findReferences(psiElement)) { 42 | final PsiElement element = reference.getElement(); 43 | if (element instanceof StringLiteralExpression) { 44 | renders.add(element.getParent()); 45 | } 46 | } 47 | } 48 | 49 | @Nullable 50 | @Override 51 | public Runnable getPostRenameCallback(PsiElement psiElement, String s, RefactoringElementListener refactoringElementListener) { 52 | return () -> { 53 | final Yii2SupportSettings settings = Yii2SupportSettings.getInstance(psiElement.getProject()); 54 | 55 | for (PsiElement render : renders) { 56 | final StringLiteralExpression element = (StringLiteralExpression) ((ParameterList) render).getParameters()[0]; 57 | String fileName = element.getContents(); 58 | if (fileName.endsWith("." + settings.defaultViewExtension)) { 59 | fileName = fileName.substring(0, fileName.length() - settings.defaultViewExtension.length() - 1); 60 | } 61 | 62 | fileName = element.isSingleQuote() ? "'" + fileName + "'" : "\"" + fileName + "\""; 63 | final PsiElement newValue = PhpPsiElementFactory.createFromText(psiElement.getProject(), StringLiteralExpression.class, fileName); 64 | if (newValue != null) { 65 | element.replace(newValue); 66 | } 67 | } 68 | 69 | FileBasedIndex.getInstance().requestRebuild(ViewFileIndex.identity); 70 | }; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/references/PsiReference.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.references; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiFile; 5 | import com.intellij.psi.PsiReferenceBase; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * Created by NVlad on 02.01.2017. 11 | */ 12 | public class PsiReference extends PsiReferenceBase { 13 | private final PsiFile myFile; 14 | 15 | PsiReference(@NotNull PsiElement element, PsiFile file) { 16 | super(element); 17 | myFile = file; 18 | } 19 | 20 | @Nullable 21 | @Override 22 | public PsiElement resolve() { 23 | return myFile; 24 | } 25 | 26 | @NotNull 27 | @Override 28 | public Object[] getVariants() { 29 | return new Object[0]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/references/PsiReferenceContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.references; 2 | 3 | import com.intellij.patterns.ElementPattern; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiReferenceRegistrar; 7 | import com.nvlad.yii2support.common.Patterns; 8 | import com.nvlad.yii2support.views.util.ViewUtil; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * Created by NVlad on 02.01.2017. 13 | */ 14 | public class PsiReferenceContributor extends com.intellij.psi.PsiReferenceContributor { 15 | @Override 16 | public void registerReferenceProviders(@NotNull PsiReferenceRegistrar psiReferenceRegistrar) { 17 | psiReferenceRegistrar.registerReferenceProvider(ElementPattern(), new PsiReferenceProvider()); 18 | } 19 | 20 | private static ElementPattern ElementPattern() { 21 | return PlatformPatterns.psiElement(PsiElement.class) 22 | .withSuperParent(2, Patterns.methodWithName(ViewUtil.renderMethods)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/references/PsiReferenceProvider.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.references; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.io.FileUtilRt; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiFile; 7 | import com.intellij.psi.PsiManager; 8 | import com.intellij.psi.search.GlobalSearchScope; 9 | import com.intellij.util.ProcessingContext; 10 | import com.intellij.util.indexing.FileBasedIndex; 11 | import com.nvlad.yii2support.common.PhpUtil; 12 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 13 | import com.nvlad.yii2support.views.entities.ViewInfo; 14 | import com.nvlad.yii2support.views.entities.ViewResolve; 15 | import com.nvlad.yii2support.views.entities.ViewResolveFrom; 16 | import com.nvlad.yii2support.views.index.ViewFileIndex; 17 | import com.nvlad.yii2support.views.util.ViewUtil; 18 | import org.jetbrains.annotations.NotNull; 19 | 20 | import java.util.Collection; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | 24 | /** 25 | * Created by NVlad on 02.01.2017. 26 | */ 27 | class PsiReferenceProvider extends com.intellij.psi.PsiReferenceProvider { 28 | @NotNull 29 | @Override 30 | public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) { 31 | Set references = new HashSet<>(); 32 | 33 | final ViewResolve resolve = ViewUtil.resolveView(psiElement); 34 | if (resolve != null) { 35 | Project project = psiElement.getProject(); 36 | 37 | String key = resolve.key; 38 | if (FileUtilRt.getExtension(key).isEmpty()) { 39 | key = key + '.' + Yii2SupportSettings.getInstance(psiElement.getProject()).defaultViewExtension; 40 | } 41 | 42 | final Collection views = FileBasedIndex.getInstance() 43 | .getValues(ViewFileIndex.identity, key, GlobalSearchScope.projectScope(project)); 44 | 45 | if (views.size() > 0) { 46 | boolean localViewSearch = false; 47 | if (resolve.from == ViewResolveFrom.View) { 48 | final String value = PhpUtil.getValue(psiElement); 49 | localViewSearch = !value.startsWith("@") && !value.startsWith("//"); 50 | } 51 | for (ViewInfo view : views) { 52 | if (!resolve.application.equals(view.application)) { 53 | continue; 54 | } 55 | 56 | if (localViewSearch && !resolve.theme.equals(view.theme)) { 57 | continue; 58 | } 59 | 60 | PsiFile file = PsiManager.getInstance(project).findFile(view.getVirtualFile()); 61 | if (file != null) { 62 | references.add(new PsiReference(psiElement, file)); 63 | } 64 | } 65 | } 66 | } 67 | 68 | return references.toArray(new PsiReference[0]); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/settings/EditPathMapEntryPanel.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.settings; 2 | 3 | import com.intellij.ui.components.JBLabel; 4 | import com.intellij.ui.components.JBPanel; 5 | import com.intellij.ui.components.JBTextField; 6 | import com.intellij.uiDesigner.core.Spacer; 7 | import org.jdesktop.swingx.VerticalLayout; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | 12 | public class EditPathMapEntryPanel extends JBPanel { 13 | private final JBLabel aliasLabel; 14 | private final JBLabel valueLabel; 15 | private final JBTextField aliasTextField; 16 | private final JBTextField valueTextField; 17 | 18 | EditPathMapEntryPanel() { 19 | setLayout(new VerticalLayout(5)); 20 | Dimension dimension = new Dimension(300, -1); 21 | setMinimumSize(dimension); 22 | 23 | aliasLabel = new JBLabel("Alias:"); 24 | aliasTextField = new JBTextField(); 25 | valueLabel = new JBLabel("Path:"); 26 | valueTextField = new JBTextField(); 27 | 28 | add(aliasLabel); 29 | add(aliasTextField); 30 | add(new Spacer() { 31 | @Override 32 | public Dimension getMinimumSize() { 33 | return new Dimension(0, 5); 34 | } 35 | }); 36 | add(valueLabel); 37 | add(valueTextField); 38 | } 39 | 40 | EditPathMapEntryPanel(String alias, String value) { 41 | this(); 42 | 43 | aliasTextField.setText(alias); 44 | valueTextField.setText(value); 45 | } 46 | 47 | void setAliasLabel(String label) { 48 | aliasLabel.setText(label); 49 | } 50 | 51 | void setValueLabel(String label) { 52 | valueLabel.setText(label); 53 | } 54 | 55 | String getAlias() { 56 | return aliasTextField.getText(); 57 | } 58 | 59 | String getValue() { 60 | return valueTextField.getText(); 61 | } 62 | 63 | 64 | public JComponent getPreferredFocusedComponent() { 65 | return aliasTextField; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/settings/EditThemePathMapDialog.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.settings; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.DialogWrapper; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.*; 9 | 10 | public class EditThemePathMapDialog extends DialogWrapper { 11 | private final EditPathMapEntryPanel myPanel; 12 | 13 | EditThemePathMapDialog(@Nullable Project project, String path, String alias) { 14 | super(project); 15 | setTitle("Edit Path Map"); 16 | 17 | this.setResizable(false); 18 | 19 | myPanel = new EditPathMapEntryPanel(path, alias); 20 | myPanel.setAliasLabel("Path Mask:"); 21 | myPanel.setValueLabel("Alias:"); 22 | 23 | init(); 24 | } 25 | 26 | @NotNull 27 | public String getPath() { 28 | return myPanel.getAlias(); 29 | } 30 | 31 | @NotNull 32 | public String getAlias() { 33 | return myPanel.getValue(); 34 | } 35 | 36 | @Nullable 37 | @Override 38 | protected JComponent createCenterPanel() { 39 | return myPanel; 40 | } 41 | 42 | @Nullable 43 | @Override 44 | public JComponent getPreferredFocusedComponent() { 45 | return myPanel.getPreferredFocusedComponent(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/settings/ThemePathMapTableModel.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.settings; 2 | 3 | import com.intellij.ui.AddEditRemovePanel; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.Map; 7 | 8 | public class ThemePathMapTableModel extends AddEditRemovePanel.TableModel> { 9 | @Override 10 | public int getColumnCount() { 11 | return 2; 12 | } 13 | 14 | @Nullable 15 | @Override 16 | public String getColumnName(int i) { 17 | return i == 0 ? "Path" : "Alias"; 18 | } 19 | 20 | @Override 21 | public Object getField(Map.Entry entry, int i) { 22 | return i == 0 ? entry.getKey() : entry.getValue(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/settings/ViewSettings.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/settings/ViewSettings.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.settings; 2 | 3 | import com.intellij.openapi.options.Configurable; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.util.indexing.FileBasedIndex; 6 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 7 | import com.nvlad.yii2support.views.index.ViewFileIndex; 8 | import com.nvlad.yii2support.views.util.ViewUtil; 9 | import org.jetbrains.annotations.Nls; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import javax.swing.*; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public class ViewSettings implements Configurable { 18 | private final Yii2SupportSettings mySettings; 19 | private Project myProject; 20 | private JPanel mainPanel; 21 | private JPanel viewPathMap; 22 | private JTextField defaultViewClass; 23 | private JComboBox defaultViewFileExt; 24 | private final List> currentThemePathMap; 25 | 26 | public ViewSettings(Project project) { 27 | myProject = project; 28 | mySettings = Yii2SupportSettings.getInstance(project); 29 | 30 | currentThemePathMap = new ArrayList<>(mySettings.viewPathMap.entrySet()); 31 | ((ThemePathMapPanel) viewPathMap).setData(new ArrayList<>(mySettings.viewPathMap.entrySet())); 32 | 33 | defaultViewClass.setText(mySettings.defaultViewClass); 34 | defaultViewFileExt.getModel().setSelectedItem(mySettings.defaultViewExtension); 35 | } 36 | 37 | @Nls 38 | @Override 39 | public String getDisplayName() { 40 | return "Views"; 41 | } 42 | 43 | @Nullable 44 | @Override 45 | public String getHelpTopic() { 46 | return null; 47 | } 48 | 49 | @Nullable 50 | @Override 51 | public JComponent createComponent() { 52 | return mainPanel; 53 | } 54 | 55 | @Override 56 | public boolean isModified() { 57 | return !mySettings.defaultViewClass.equals(defaultViewClass.getText()) 58 | || !mySettings.defaultViewExtension.equals(defaultViewFileExt.getModel().getSelectedItem()) 59 | || ((ThemePathMapPanel) viewPathMap).getData().hashCode() != currentThemePathMap.hashCode(); 60 | } 61 | 62 | @Override 63 | public void apply() { 64 | if (((ThemePathMapPanel) viewPathMap).getData().hashCode() != currentThemePathMap.hashCode() 65 | || !mySettings.defaultViewExtension.equals(defaultViewFileExt.getModel().getSelectedItem())) { 66 | currentThemePathMap.clear(); 67 | currentThemePathMap.addAll(((ThemePathMapPanel) viewPathMap).getData()); 68 | mySettings.viewPathMap.clear(); 69 | for (Map.Entry entry : currentThemePathMap) { 70 | mySettings.viewPathMap.put(entry.getKey(), entry.getValue()); 71 | } 72 | mySettings.defaultViewExtension = defaultViewFileExt.getModel().getSelectedItem().toString(); 73 | 74 | ViewUtil.resetPathMapPatterns(myProject); 75 | FileBasedIndex.getInstance().requestRebuild(ViewFileIndex.identity); 76 | } 77 | mySettings.defaultViewClass = defaultViewClass.getText(); 78 | } 79 | 80 | @Override 81 | public void reset() { 82 | List> data = ((ThemePathMapPanel) viewPathMap).getData(); 83 | data.clear(); 84 | data.addAll(currentThemePathMap); 85 | viewPathMap.updateUI(); 86 | 87 | defaultViewClass.setText(mySettings.defaultViewClass); 88 | defaultViewFileExt.getModel().setSelectedItem(mySettings.defaultViewExtension); 89 | } 90 | 91 | private void createUIComponents() { 92 | // TODO: place custom component creation code here 93 | viewPathMap = new ThemePathMapPanel(myProject); 94 | } 95 | 96 | @Override 97 | public void disposeUIResources() { 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/views/util/RenderUtil.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.views.util; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.jetbrains.php.lang.psi.elements.*; 5 | import com.jetbrains.php.lang.psi.resolve.types.PhpType; 6 | import com.nvlad.yii2support.utils.Yii2SupportSettings; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.LinkedHashMap; 10 | import java.util.Map; 11 | 12 | public class RenderUtil { 13 | @NotNull 14 | public static Map getViewArguments(MethodReference reference) { 15 | final Map result = new LinkedHashMap<>(); 16 | result.put("this", new PhpType.PhpTypeBuilder().add(Yii2SupportSettings.getInstance(reference.getProject()).defaultViewClass).build()); 17 | 18 | ParameterList parameterList = reference.getParameterList(); 19 | if (parameterList == null) { 20 | return result; 21 | } 22 | 23 | if (parameterList.getParameters().length == 1) { 24 | return result; 25 | } 26 | 27 | final PsiElement parameter = parameterList.getParameters()[1]; 28 | if (parameter instanceof ArrayCreationExpression) { 29 | final ArrayCreationExpression array = (ArrayCreationExpression) parameter; 30 | for (ArrayHashElement item : array.getHashElements()) { 31 | PhpPsiElement keyElement = item.getKey(); 32 | 33 | String key; 34 | if (keyElement instanceof StringLiteralExpression) { 35 | key = ((StringLiteralExpression) keyElement).getContents(); 36 | } else { 37 | continue; 38 | } 39 | 40 | PhpType valueType; 41 | final PhpPsiElement valueElement = item.getValue(); 42 | if (valueElement instanceof PhpExpression) { 43 | valueType = ((PhpTypedElement) valueElement).getType().global(valueElement.getProject()); 44 | } else { 45 | continue; 46 | } 47 | 48 | result.put(key, valueType); 49 | } 50 | 51 | return result; 52 | } 53 | 54 | return result; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/com/nvlad/yii2support/widgetsconfig/WidgetConfigCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.widgetsconfig; 2 | 3 | import com.intellij.codeInsight.completion.CompletionContributor; 4 | import com.intellij.codeInsight.completion.CompletionType; 5 | import com.intellij.patterns.ElementPattern; 6 | import com.intellij.patterns.PlatformPatterns; 7 | import com.intellij.psi.PsiElement; 8 | import com.jetbrains.php.lang.psi.elements.*; 9 | import com.nvlad.yii2support.common.Patterns; 10 | 11 | public class WidgetConfigCompletionContributor extends CompletionContributor { 12 | public WidgetConfigCompletionContributor() { 13 | extend(CompletionType.BASIC, ElementPattern(), new WidgetConfigCompletionProvider()); 14 | } 15 | 16 | private static ElementPattern ElementPattern() { 17 | return PlatformPatterns.psiElement() 18 | .withParent(PlatformPatterns.psiElement(StringLiteralExpression.class) 19 | .withParent(PlatformPatterns.or( 20 | PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class), 21 | PlatformPatterns.psiElement().withParent(ArrayAccessExpression.class), 22 | PlatformPatterns.psiElement(PhpPsiElement.class), 23 | Patterns.withHashKey().withParent(PlatformPatterns.psiElement().withParent(ArrayCreationExpression.class)) 24 | ) 25 | ) 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/common/StringUtilsTest.java: -------------------------------------------------------------------------------- 1 | package com.nvlad.yii2support.common; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class StringUtilsTest { 8 | @Test 9 | public void camelToId() { 10 | assertEquals(StringUtils.CamelToId("testId"), "test_id"); 11 | } 12 | 13 | @Test 14 | public void camelToIdWithSeparator() { 15 | assertEquals(StringUtils.CamelToId("testId", "+"), "test+id"); 16 | 17 | assertEquals(StringUtils.CamelToId("testId", ""), "testid"); 18 | 19 | assertEquals(StringUtils.CamelToId("testId", "-"), "test-id"); 20 | 21 | assertEquals(StringUtils.CamelToId("testId", "_"), "test_id"); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/database/ActiveQueryNoConnection.java: -------------------------------------------------------------------------------- 1 | //package com.nvlad.yii2support.database; 2 | // 3 | //import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; 4 | //import com.jetbrains.php.lang.PhpFileType; 5 | // 6 | ///** 7 | // * Created by oleg on 05.04.2017. 8 | // */ 9 | //public class ActiveQueryNoConnection extends LightCodeInsightFixtureTestCase { 10 | // protected void setUp() throws Exception { 11 | // super.setUp(); 12 | // myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("classes.php")); 13 | // } 14 | // 15 | // @Override 16 | // protected String getTestDataPath() { 17 | // return "tests/com/nvlad/yii2support/database/fixtures"; 18 | // } 19 | // 20 | // public void testQuery_Empty() { 21 | // myFixture.configureByText(PhpFileType.INSTANCE, "where('"); 23 | // myFixture.completeBasic(); 24 | // assertEquals(3, myFixture.getLookupElementStrings().size()); 25 | // } 26 | //} 27 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/database/fixtures/TestModel.java: -------------------------------------------------------------------------------- 1 | //package com.nvlad.yii2support.database.fixtures; 2 | // 3 | //import com.intellij.database.model.*; 4 | //import com.intellij.database.psi.DbElement; 5 | //import com.intellij.database.util.Casing; 6 | //import com.intellij.database.util.DasUtil; 7 | //import com.intellij.openapi.project.Project; 8 | //import com.intellij.util.containers.JBIterable; 9 | //import com.intellij.util.containers.JBTreeTraverser; 10 | //import org.jetbrains.annotations.NotNull; 11 | //import org.jetbrains.annotations.Nullable; 12 | // 13 | //import java.util.ArrayList; 14 | //import java.util.Iterator; 15 | //import java.util.LinkedList; 16 | //import java.util.List; 17 | // 18 | ///** 19 | // * Created by oleg on 03.04.2017. 20 | // */ 21 | //public class TestModel implements DasModel { 22 | // private Project myProject; 23 | // private TestNamespace myNamespace; 24 | // private List myList; 25 | // public TestModel(Project project) { 26 | // this.myProject = project; 27 | // 28 | // myList = new LinkedList<>(); 29 | // myNamespace = new TestNamespace(); 30 | // 31 | // TestTable personTestTable = new TestTable("person", myProject); 32 | // personTestTable.addColumn(new TestColumn(personTestTable, "name", myProject)); 33 | // personTestTable.addColumn(new TestColumn(personTestTable, "surname", myProject)); 34 | // personTestTable.addColumn(new TestColumn(personTestTable, "birth_date", myProject)); 35 | // myList.add(personTestTable); 36 | // myNamespace.addTable(personTestTable); 37 | // 38 | // TestTable addressTestTable = new TestTable("address", myProject); 39 | // addressTestTable.addColumn(new TestColumn(addressTestTable, "street", myProject)); 40 | // addressTestTable.addColumn(new TestColumn(addressTestTable, "city", myProject)); 41 | // myList.add(addressTestTable); 42 | // myNamespace.addTable(addressTestTable); 43 | // } 44 | // 45 | // @NotNull 46 | // @Override 47 | // public JBIterable getModelRoots() { 48 | // List roots = new ArrayList<>(1); 49 | // roots.add(myNamespace); 50 | // 51 | // return new JBIterable() { 52 | // @NotNull 53 | // @Override 54 | // public Iterator iterator() { 55 | // return roots.iterator(); 56 | // } 57 | // }; 58 | // } 59 | // 60 | // @Nullable 61 | // @Override 62 | // public DasNamespace getCurrentRootNamespace() { 63 | // return new DasNamespace() { 64 | // @NotNull 65 | // @Override 66 | // public ObjectKind getKind() { 67 | // return null; 68 | // } 69 | // 70 | // @NotNull 71 | // @Override 72 | // public String getName() { 73 | // return null; 74 | // } 75 | // 76 | // @Nullable 77 | // @Override 78 | // public String getComment() { 79 | // return null; 80 | // } 81 | // 82 | // @Nullable 83 | // @Override 84 | // public DasObject getDbParent() { 85 | // return null; 86 | // } 87 | // 88 | // @NotNull 89 | // @Override 90 | // public JBIterable getDbChildren(@NotNull Class clazz, @NotNull ObjectKind kind) { 91 | // return null; 92 | // } 93 | // }; 94 | // } 95 | // 96 | // @NotNull 97 | // @Override 98 | // public JBTreeTraverser traverser() { 99 | // return DasUtil.dasTraverser().withRoots(myList); 100 | //// return new JBTreeTraverser(o -> () -> myList.iterator()); 101 | // } 102 | // 103 | // @NotNull 104 | // @Override 105 | // public JBIterable getExportedKeys(DasTable table) { 106 | // return null; 107 | // } 108 | // 109 | // @NotNull 110 | // @Override 111 | // public Casing getCasing(@NotNull ObjectKind kind, @Nullable DasObject context) { 112 | // return null; 113 | // } 114 | //} 115 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/database/fixtures/classes.php: -------------------------------------------------------------------------------- 1 | ']) ;\n" + 27 | ";"); 28 | myFixture.completeBasic(); 29 | assertEquals(myFixture.getLookupElementStrings().size(), 2); 30 | } 31 | 32 | @Test 33 | public void testCompletionWidget_begin() { 34 | myFixture.configureByText(PhpFileType.INSTANCE, "']) ;\n" + 36 | ";"); 37 | myFixture.completeBasic(); 38 | assertEquals(myFixture.getLookupElementStrings().size(), 2); 39 | } 40 | 41 | @Test 42 | public void testCompletionObject_create() { 43 | myFixture.configureByText(PhpFileType.INSTANCE, "']) ;\n" + 45 | ";"); 46 | myFixture.completeBasic(); 47 | assertEquals(myFixture.getLookupElementStrings().size(), 2); 48 | } 49 | 50 | @Test 51 | public void testCompletion_createObject() { 52 | myFixture.configureByText(PhpFileType.INSTANCE, "']) ;\n" + 54 | ";"); 55 | myFixture.completeBasic(); 56 | assertEquals(myFixture.getLookupElementStrings().size(), 2); 57 | } 58 | 59 | @Test 60 | public void testCompletionInConfigAndSubObject() { 61 | myFixture.configureByText(PhpFileType.INSTANCE, " [ 'subobject' => ['']] ;\n" + 63 | ";"); 64 | myFixture.completeBasic(); 65 | assertEquals(3, myFixture.getLookupElementStrings().size()); 66 | } 67 | 68 | @Test 69 | public void testCompletionYii_createObject() { 70 | myFixture.configureByText(PhpFileType.INSTANCE, "']) ;\n" + 72 | ";"); 73 | myFixture.completeBasic(); 74 | assertEquals(myFixture.getLookupElementStrings().size(), 3); 75 | } 76 | 77 | @Test 78 | public void testCompletionYii_gridColumns() { 79 | myFixture.configureByText(PhpFileType.INSTANCE, " [['']] "); 82 | myFixture.completeBasic(); 83 | assertEquals(myFixture.getLookupElementStrings().size(), 2); 84 | } 85 | 86 | @Test 87 | public void testCompletionYii_arrayAsTypedParam() { 88 | myFixture.configureByText(PhpFileType.INSTANCE, "setColumn(['']) "); 90 | myFixture.completeBasic(); 91 | assertEquals(myFixture.getLookupElementStrings().size(), 2); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/objectfactory/fixtures/classes.php: -------------------------------------------------------------------------------- 1 | lookupElements = basicCompletionResultsForFile("testControllerRedirectMethod.php"); 28 | List expected = buildFixtureActionList("test/redirect-test"); 29 | 30 | assertArrayEquals(expected.toArray(), lookupElements.toArray()); 31 | } 32 | 33 | @Test 34 | public void testControllerRedirectMethodArrayArgument() { 35 | List lookupElements = basicCompletionResultsForFile("testControllerRedirectMethodArrayArgument.php"); 36 | List expected = buildFixtureActionList("test/redirect-test"); 37 | 38 | assertArrayEquals(expected.toArray(), lookupElements.toArray()); 39 | } 40 | 41 | @Test 42 | public void testUrlTo() { 43 | List lookupElements = basicCompletionResultsForFile("testUrlTo.php"); 44 | List expected = buildFixtureActionList(); 45 | expected.add(0,""); // Seems a bug since 2020.3.3 46 | 47 | assertArrayEquals(expected.toArray(), lookupElements.toArray()); 48 | } 49 | 50 | @Test 51 | public void testUrlParameterCompletion() { 52 | List lookupElements = basicCompletionResultsForFile("testUrlParameterCompletion.php"); 53 | 54 | List expected = new ArrayList<>(); 55 | expected.add("id"); 56 | expected.add("action"); 57 | expected.sort(String::compareTo); 58 | 59 | assertArrayEquals(expected.toArray(), lookupElements.toArray()); 60 | } 61 | 62 | @Test 63 | public void testUrlToArrayArgument() { 64 | List lookupElements = basicCompletionResultsForFile("testUrlToArrayArgument.php"); 65 | List expected = buildFixtureActionList(); 66 | 67 | assertArrayEquals(expected.toArray(), lookupElements.toArray()); 68 | } 69 | 70 | @Test 71 | public void testUrlRemember() { 72 | List lookupElements = basicCompletionResultsForFile("testUrlRemember.php"); 73 | List expected = buildFixtureActionList(); 74 | expected.add(0,""); // Seems a bug since 2020.3.3 75 | 76 | assertArrayEquals(expected.toArray(), lookupElements.toArray()); 77 | } 78 | 79 | @Test 80 | public void testUrlRememberArrayArgument() { 81 | List lookupElements = basicCompletionResultsForFile("testUrlRememberArrayArgument.php"); 82 | List expected = buildFixtureActionList(); 83 | 84 | assertArrayEquals(expected.toArray(), lookupElements.toArray()); 85 | } 86 | 87 | private List buildFixtureActionList() { 88 | List result = new ArrayList<>(); 89 | result.add("home/index"); 90 | result.add("home/about"); 91 | result.add("home/transactions"); 92 | result.add("home/car-controller"); 93 | result.add("room-controller/index"); 94 | result.add("room-controller/transactions"); 95 | result.add("room-controller/tv-controller"); 96 | result.sort(String::compareTo); 97 | 98 | return result; 99 | } 100 | 101 | private List buildFixtureActionList(String ...extraActions) { 102 | List result = buildFixtureActionList(); 103 | Collections.addAll(result, extraActions); 104 | result.sort(String::compareTo); 105 | 106 | return result; 107 | } 108 | 109 | private List basicCompletionResultsForFile(String filePath) { 110 | myFixture.configureByFile(filePath); 111 | myFixture.completeBasic(); 112 | 113 | List lookupElements = myFixture.getLookupElementStrings(); 114 | assertNotNull(lookupElements); 115 | lookupElements.sort(String::compareTo); 116 | 117 | return lookupElements; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/url/fixtures/classes.php: -------------------------------------------------------------------------------- 1 | redirect(''); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/url/fixtures/testControllerRedirectMethodArrayArgument.php: -------------------------------------------------------------------------------- 1 | redirect(['']); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/url/fixtures/testUrlParameterCompletion.php: -------------------------------------------------------------------------------- 1 | ']) ?> 2 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/url/fixtures/testUrlRemember.php: -------------------------------------------------------------------------------- 1 | ') ?> 2 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/url/fixtures/testUrlRememberArrayArgument.php: -------------------------------------------------------------------------------- 1 | ']) ?> 2 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/url/fixtures/testUrlTo.php: -------------------------------------------------------------------------------- 1 | ') ?> 2 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/url/fixtures/testUrlToArrayArgument.php: -------------------------------------------------------------------------------- 1 | ']) ?> 2 | -------------------------------------------------------------------------------- /tests/com/nvlad/yii2support/validation/fixtures/classes.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | --------------------------------------------------------------------------------