├── .gitignore ├── .idea ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── LICENSE ├── README.md ├── img ├── config.gif ├── config.png ├── dajian.png ├── dajian1.png ├── db.gif ├── db.png ├── log.gif ├── model.gif ├── model.png ├── modelType.png ├── route.gif ├── route.png ├── tplt.gif ├── view.gif └── view.png ├── intellij笔记.xmind ├── out └── production │ └── plugin │ ├── META-INF │ └── plugin.xml │ └── icons │ ├── config_value.png │ ├── config_value@2x.png │ ├── controller_line_marker.png │ ├── laravel.png │ ├── laravel@2x.png │ ├── laravel_dec.png │ ├── laravel_dec@2x.png │ ├── route.png │ ├── route@2x.png │ ├── translation.png │ └── translation@2x.png ├── plugin.iml ├── plugin.jar ├── resources ├── META-INF │ └── plugin.xml ├── icons │ ├── 1.png │ ├── config_value.png │ ├── config_value@2x.png │ ├── controller_line_marker.png │ ├── laravel.png │ ├── laravel@2x.png │ ├── laravel_dec.png │ ├── laravel_dec@2x.png │ ├── log.png │ ├── route.png │ ├── route@2x.png │ ├── translation.png │ └── translation@2x.png └── liveTemplates │ └── ThinkPHP-5.xml ├── src └── pers │ └── fw │ └── tplugin │ ├── beans │ ├── ArrayKeyVisitor.java │ ├── ArrayMapVisitor.java │ ├── Bean.java │ ├── Config.java │ ├── LaravelIcons.java │ ├── LookupElem.java │ ├── MethodReferenceBag.java │ ├── ParameterBag.java │ └── Setting.java │ ├── config │ ├── AppConfigReferences.java │ ├── ArrayReturnPsiRecursiveVisitor.java │ ├── CollectProjectUniqueKeys.java │ ├── ConfigFileUtil.java │ ├── ConfigKeyStubIndex.java │ └── ProviderGotoCompletion.java │ ├── db │ ├── Column.java │ ├── DbReference.java │ ├── DbTableUtil.java │ ├── TableBean.java │ ├── Tables.java │ └── TablesVisitor.java │ ├── inter │ ├── CompletionContributorParameter.java │ ├── GotoCompletionContributor.java │ ├── GotoCompletionLanguageRegistrar.java │ ├── GotoCompletionProvider.java │ ├── GotoCompletionProviderInterface.java │ ├── GotoCompletionRegistrar.java │ └── GotoCompletionRegistrarParameter.java │ ├── log │ ├── LogUtil.java │ ├── MyToolWindow.form │ ├── MyToolWindowFactory.java │ └── ShowLog.java │ ├── main │ ├── Completer.java │ ├── Documenter.java │ ├── GotoHandler.java │ ├── MyTypeProvider.java │ └── Templater.java │ ├── model │ ├── ControllerStubIndex.java │ ├── ModelReference.java │ ├── ModelStubIndex.java │ └── ModelUtil.java │ ├── router │ ├── PhpControllerVisitor.java │ ├── RouteUtil.java │ ├── RouteValStubIndex.java │ └── RouterReference.java │ ├── util │ ├── GotoCompletionUtil.java │ ├── LaravelProjectComponent.java │ ├── LaravelSettings.java │ ├── MethodMatcher.java │ ├── PhpElementsUtil.java │ ├── PsiElementUtil.java │ ├── PsiElementUtils.java │ ├── Symfony2InterfacesUtil.java │ ├── Tool.java │ ├── Util.java │ └── VfsExUtil.java │ └── view │ ├── GotoController.java │ ├── TemplateUtil.java │ ├── ViewCollector.java │ ├── ViewReferences2.java │ └── dict │ ├── JsonTemplatePaths.java │ └── TemplatePath.java └── tplugin.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | plugin.iml 3 | out 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 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 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Daniel Espendiller 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | thinkPhp5 plugin 2 | ------------------------------ 3 | 4 | ## 概览 5 | 6 | 参考[Haehnchen/idea-php-laravel-plugin](https://github.com/Haehnchen/idea-php-laravel-plugin) 7 | 8 | 用于thinkphp5.0的视图,配置,路由,数据库,模型智能提示和跳转(快捷键Ctrl+B或者Ctrl+click), 及模型函数(model)返回类型分析 9 | 10 | thinkphp3版本插件,在tp3分支 11 | 12 | ## 安装 13 | 14 | 在线安装: 在phpstorm的插件中心(File->Setting->Plugins), 点击Browse repositories搜索thinkphp5 plugin安装 15 | 16 | 离线安装: 下载release文件或者根目录下的plugin.zip/plugin.jar, 在phpstorm的插件中心,点击Install plugin from disk,选择下载文件安装 17 | 18 | ## 使用 19 | 20 | 配置 21 | 22 | 提示方法config(),Config::get(),Config::set(), 23 | 扫描/config和/application目录下的配置文件进行提示, 可跳转到配置文件源位置 24 | ![img](https://github.com/fw6669998/Thinkphp5-Plugin/blob/master/img/config.gif) 25 | 26 | 视图 27 | 28 | 提示方法$this->fetch(), view(), 29 | 可跳转到页面(html)位置 30 | ![img](https://github.com/fw6669998/Thinkphp5-Plugin/blob/master/img/view.gif) 31 | 32 | 路由 33 | 34 | 使用Route::get/post/put/delete/any在任何位置提示, 35 | 使用return ['test' => ['in', ['method' => 'post']]]格式,在application和config目录下文件名带有route的文件中进行提示 36 | ![img](https://github.com/fw6669998/Thinkphp5-Plugin/blob/master/img/route.gif) 37 | 38 | 模型 39 | 40 | 提示方法model(),Loader::model(), 41 | 使用model()方法会对分析返回值类型进行赋给变量,最终类型为实际模型类型,而不是Model类型 42 | ![img](https://github.com/fw6669998/Thinkphp5-Plugin/blob/master/img/model.gif) 43 | 44 | 数据库 45 | 46 | [配置phpstorm数据库连接](https://jingyan.baidu.com/article/0a52e3f4cee074bf62ed7208.html) 47 | 48 | 首先配置请数据库连接,如果不会请点击上面的衔接 49 | 50 | 提示衔接: 51 | 如果只有一条数据库连接,则该数据库为提示连接, 52 | 如果有多条连接将需要提示的数据库连接命名为包含work的命名的连接, //重命名连接, 连接->右键->rename 53 | 54 | 数据库会在方法的代码中收集表进行字段提示, 55 | 与数据表相关的方法会进行表提示, 与数据库字段相关的方法会进行字段提示, 56 | 以$where,$field,$row为开头的数组变量会进行字段提示,可在配置文件中进行配置 57 | ![img](https://github.com/fw6669998/Thinkphp5-Plugin/blob/master/img/db.gif) 58 | 59 | 日志 60 | 61 | 该功能默认关闭(略感鸡肋), 要开启请在插件配置文件中打开 62 | 通过监听日志文件, 打印日志内容, 63 | 筛选日志,在插件配置文件中配置筛选,通过前缀或正则进行匹配 64 | ![img](https://github.com/fw6669998/Thinkphp5-Plugin/blob/master/img/log.gif) 65 | 66 | 插件配置文件 67 | 68 | 该文件用于配置某些功能, 不配置也可以没有关系 69 | 将配置文件放在项目即可生效, //配置模板及说明:tplugin.json文件 70 | 71 | 模板标签 live template 72 | 73 | tp的模板标签,输入关键字再按tab展开 74 | 如需修改标签内容在File->Setting->Editor->Live templates->Thinkphp-5中选择修改 75 | 模板标签配置数据来源: http://www.thinkphp.cn/code/4441.html 76 | ![img](https://github.com/fw6669998/Thinkphp5-Plugin/blob/master/img/tplt.gif) 77 | 78 | ## 注意事项 79 | 当使用tp5.1的门面类时无法提示和跳转,在文件头部加上类的全限定名 例如:use \think\facade\Route 80 | 81 | 更新插件后建议点下 File->invalidate caches/restart 更新下缓存 82 | 83 | -------------------------------------------------------------------------------- /img/config.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/config.gif -------------------------------------------------------------------------------- /img/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/config.png -------------------------------------------------------------------------------- /img/dajian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/dajian.png -------------------------------------------------------------------------------- /img/dajian1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/dajian1.png -------------------------------------------------------------------------------- /img/db.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/db.gif -------------------------------------------------------------------------------- /img/db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/db.png -------------------------------------------------------------------------------- /img/log.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/log.gif -------------------------------------------------------------------------------- /img/model.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/model.gif -------------------------------------------------------------------------------- /img/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/model.png -------------------------------------------------------------------------------- /img/modelType.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/modelType.png -------------------------------------------------------------------------------- /img/route.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/route.gif -------------------------------------------------------------------------------- /img/route.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/route.png -------------------------------------------------------------------------------- /img/tplt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/tplt.gif -------------------------------------------------------------------------------- /img/view.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/view.gif -------------------------------------------------------------------------------- /img/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/img/view.png -------------------------------------------------------------------------------- /intellij笔记.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/intellij笔记.xmind -------------------------------------------------------------------------------- /out/production/plugin/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | pers.fw.tplugin 3 | Thinkphp5 Plugin 4 | 1.3.8 5 | Source code and tutorials/源码及详细说明 6 | View, Configuration, Routing, Database, Model Intelligent Tips and Jumps (shortcut keys F3 or f12), and Model Function return type analysis for ThinkPHP 5.0

8 |

用于thinkphp5.0的视图,配置,路由,数据库,模型智能提示和跳转(快捷键Ctrl+B),及模型函数(model)返回类型分析

9 |

10 | ]]>
11 | 14 | 15 | com.jetbrains.php 16 | com.intellij.database 17 | com.intellij.modules.platform 18 | 19 | 20 | 21 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 |
-------------------------------------------------------------------------------- /out/production/plugin/icons/config_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/config_value.png -------------------------------------------------------------------------------- /out/production/plugin/icons/config_value@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/config_value@2x.png -------------------------------------------------------------------------------- /out/production/plugin/icons/controller_line_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/controller_line_marker.png -------------------------------------------------------------------------------- /out/production/plugin/icons/laravel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/laravel.png -------------------------------------------------------------------------------- /out/production/plugin/icons/laravel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/laravel@2x.png -------------------------------------------------------------------------------- /out/production/plugin/icons/laravel_dec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/laravel_dec.png -------------------------------------------------------------------------------- /out/production/plugin/icons/laravel_dec@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/laravel_dec@2x.png -------------------------------------------------------------------------------- /out/production/plugin/icons/route.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/route.png -------------------------------------------------------------------------------- /out/production/plugin/icons/route@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/route@2x.png -------------------------------------------------------------------------------- /out/production/plugin/icons/translation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/translation.png -------------------------------------------------------------------------------- /out/production/plugin/icons/translation@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/out/production/plugin/icons/translation@2x.png -------------------------------------------------------------------------------- /plugin.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /plugin.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/plugin.jar -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | pers.fw.tplugin 3 | Thinkphp5 Helper 4 | 1.3.8 5 | Source code and tutorials/源码及详细说明 6 | View, Configuration, Routing, Database, Model Intelligent Tips and Jumps (shortcut keys F3 or f12), and Model Function return type analysis for ThinkPHP 5.0

8 |

用于thinkphp5.0的视图,配置,路由,数据库,模型智能提示和跳转(快捷键Ctrl+B),及模型函数(model)返回类型分析

9 |

10 | ]]>
11 | com.jetbrains.php 12 | com.intellij.database 13 | com.intellij.modules.platform 14 | 15 | 16 | 17 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 |
-------------------------------------------------------------------------------- /resources/icons/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/1.png -------------------------------------------------------------------------------- /resources/icons/config_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/config_value.png -------------------------------------------------------------------------------- /resources/icons/config_value@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/config_value@2x.png -------------------------------------------------------------------------------- /resources/icons/controller_line_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/controller_line_marker.png -------------------------------------------------------------------------------- /resources/icons/laravel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/laravel.png -------------------------------------------------------------------------------- /resources/icons/laravel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/laravel@2x.png -------------------------------------------------------------------------------- /resources/icons/laravel_dec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/laravel_dec.png -------------------------------------------------------------------------------- /resources/icons/laravel_dec@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/laravel_dec@2x.png -------------------------------------------------------------------------------- /resources/icons/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/log.png -------------------------------------------------------------------------------- /resources/icons/route.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/route.png -------------------------------------------------------------------------------- /resources/icons/route@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/route@2x.png -------------------------------------------------------------------------------- /resources/icons/translation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/translation.png -------------------------------------------------------------------------------- /resources/icons/translation@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fw6669998/Thinkphp5-Plugin/d2c80ccdf6e9941b11627fcca317052acfdbb1f3/resources/icons/translation@2x.png -------------------------------------------------------------------------------- /resources/liveTemplates/ThinkPHP-5.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | 20 | 26 | 32 | 37 | 42 | 47 | 54 | 61 | 67 | 73 | 79 | 85 | 91 | 97 | 103 | 110 | 117 | 124 | 131 | 138 | 145 | 152 | 159 | 166 | 173 | 180 | 187 | 194 | 201 | 208 | 215 | 222 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/ArrayKeyVisitor.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | import com.intellij.psi.PsiElement; 4 | 5 | /** 6 | * @author Daniel Espendiller 7 | */ 8 | public interface ArrayKeyVisitor { 9 | void visit(String key, PsiElement psiKey, boolean isRootElement); 10 | } 11 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/ArrayMapVisitor.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | public interface ArrayMapVisitor { 4 | void visit(String key,String value); 5 | } 6 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/Bean.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | public class Bean { 4 | public static class ModelTable { 5 | public int type = 1; 6 | public String tableName = ""; 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/Config.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Config { //配置类 7 | public String[] dbMethod = {}; //数据库提示方法 8 | public String[] dbArrMethod={}; //数据库数组参数提示方法 9 | public String[] dbVar ={}; //数据库提示字段提示变量 10 | 11 | public boolean logEnable = false; //日志开关 12 | public String[] logPrefix = {}; //显示日志内容的前缀 13 | public String[] logRegex = {}; //显示日志内容,根据正则匹配 14 | } 15 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/LaravelIcons.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | import java.util.Calendar; 7 | 8 | /** 9 | * @author Daniel Espendiller 10 | */ 11 | public class LaravelIcons { 12 | 13 | public static final Icon LARAVEL; 14 | 15 | static { 16 | if(Calendar.getInstance().get(Calendar.MONTH) == 11 && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) > 7) { 17 | LARAVEL = IconLoader.getIcon("/icons/laravel_dec.png"); 18 | } else { 19 | LARAVEL = IconLoader.getIcon("/icons/laravel.png"); 20 | } 21 | } 22 | public static final Icon CONFIG = IconLoader.getIcon("/icons/config_value.png"); 23 | public static final Icon ROUTE = IconLoader.getIcon("/icons/route.png"); 24 | public static final Icon TRANSLATION = IconLoader.getIcon("/icons/translation.png"); 25 | public static final Icon TEMPLATE_CONTROLLER_LINE_MARKER = IconLoader.getIcon("/icons/controller_line_marker.png"); 26 | public static final Icon log = IconLoader.getIcon("/runConfigurations/trackTests.png"); 27 | } 28 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/LookupElem.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public class LookupElem extends com.intellij.codeInsight.lookup.LookupElement { 6 | private String str; 7 | 8 | public LookupElem(String str) { 9 | this.str = str; 10 | } 11 | 12 | @NotNull 13 | @Override 14 | public String getLookupString() { 15 | return this.str; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/MethodReferenceBag.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | import pers.fw.tplugin.beans.ParameterBag; 4 | import com.jetbrains.php.lang.psi.elements.MethodReference; 5 | import com.jetbrains.php.lang.psi.elements.ParameterList; 6 | 7 | public class MethodReferenceBag { 8 | 9 | final private ParameterList parameterList; 10 | final private MethodReference methodReference; 11 | final private ParameterBag parameterBag; 12 | 13 | public MethodReferenceBag(ParameterList parameterList, MethodReference methodReference, ParameterBag parameterBag) { 14 | this.parameterList = parameterList; 15 | this.methodReference = methodReference; 16 | this.parameterBag = parameterBag; 17 | } 18 | 19 | public ParameterList getParameterList() { 20 | return parameterList; 21 | } 22 | 23 | public MethodReference getMethodReference() { 24 | return methodReference; 25 | } 26 | 27 | public ParameterBag getParameterBag() { 28 | return parameterBag; 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/ParameterBag.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | import com.intellij.psi.PsiElement; 4 | 5 | public class ParameterBag { 6 | 7 | private int index; 8 | private PsiElement psiElement; 9 | 10 | public ParameterBag(int index, PsiElement psiElement) { 11 | this.index = index; 12 | this.psiElement = psiElement; 13 | } 14 | 15 | public int getIndex() { 16 | return index; 17 | } 18 | 19 | // public String getValue() { 20 | // return PsiElementUtils.getMethodParameter(psiElement); 21 | // } 22 | 23 | public PsiElement getElement() { 24 | return this.psiElement; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/beans/Setting.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.beans; 2 | 3 | public class Setting { 4 | public static String fileName="tplugin.json"; 5 | public static Config config = new Config(); 6 | } 7 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/config/AppConfigReferences.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.config; 2 | 3 | import pers.fw.tplugin.beans.ArrayKeyVisitor; 4 | import pers.fw.tplugin.beans.LaravelIcons; 5 | import com.intellij.codeInsight.lookup.LookupElement; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.lang.Language; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.patterns.PlatformPatterns; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.PsiManager; 13 | import com.intellij.psi.search.GlobalSearchScope; 14 | import com.intellij.util.Processor; 15 | import com.intellij.util.indexing.FileBasedIndex; 16 | import com.jetbrains.php.lang.PhpFileType; 17 | import com.jetbrains.php.lang.PhpLanguage; 18 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 19 | import pers.fw.tplugin.inter.GotoCompletionContributor; 20 | import pers.fw.tplugin.inter.GotoCompletionLanguageRegistrar; 21 | import pers.fw.tplugin.inter.GotoCompletionProvider; 22 | import pers.fw.tplugin.inter.GotoCompletionRegistrarParameter; 23 | import org.apache.commons.lang.StringUtils; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.jetbrains.annotations.Nullable; 26 | import pers.fw.tplugin.util.*; 27 | 28 | import java.util.*; 29 | 30 | public class AppConfigReferences implements GotoCompletionLanguageRegistrar { 31 | 32 | private static MethodMatcher.CallToSignature[] CONFIG = new MethodMatcher.CallToSignature[]{ 33 | new MethodMatcher.CallToSignature("\\think\\Config", "get"), 34 | new MethodMatcher.CallToSignature("\\think\\Config", "has"), 35 | new MethodMatcher.CallToSignature("\\think\\Config", "set"), 36 | new MethodMatcher.CallToSignature("\\think\\facade\\Config", "get"), 37 | new MethodMatcher.CallToSignature("\\think\\facade\\Config", "set"), 38 | new MethodMatcher.CallToSignature("\\think\\facade\\Config", "has"), 39 | // new MethodMatcher.CallToSignature("\\Illuminate\\Config\\Repository", "setParsedKey"), 40 | }; 41 | 42 | @Override 43 | public void register(GotoCompletionRegistrarParameter registrar) { 44 | registrar.register(PlatformPatterns.psiElement(), new GotoCompletionContributor() { 45 | @Nullable 46 | @Override 47 | public GotoCompletionProvider getProvider(@Nullable PsiElement psiElement) { 48 | if (psiElement == null) {// || !LaravelProjectComponent.isEnabled(psiElement)) { 49 | return null; 50 | } 51 | 52 | PsiElement parent = psiElement.getParent(); 53 | if (parent != null && (PsiElementUtil.isFunctionReference(parent, "config", 0) 54 | || Util.isHintMethod(parent, CONFIG, 0, true))) { 55 | return new ConfigKeyProvider(parent); 56 | } 57 | 58 | return null; 59 | } 60 | }); 61 | } 62 | 63 | @Override 64 | public boolean support(@NotNull Language language) { 65 | return PhpLanguage.INSTANCE == language; 66 | } 67 | 68 | private static class ConfigKeyProvider extends GotoCompletionProvider { 69 | 70 | public ConfigKeyProvider(PsiElement element) { 71 | super(element); 72 | } 73 | 74 | /** 75 | * 获取提示信息 76 | * 77 | * @return 返回提示集合 78 | */ 79 | @NotNull 80 | @Override 81 | public Collection getLookupElements() { 82 | 83 | final Collection lookupElements = new ArrayList<>(); 84 | 85 | CollectProjectUniqueKeys ymlProjectProcessor = new CollectProjectUniqueKeys(getProject(), ConfigKeyStubIndex.KEY); 86 | //扫描文件获取key, 放入ymlProjectProcessor 87 | FileBasedIndex.getInstance().processAllKeys(ConfigKeyStubIndex.KEY, ymlProjectProcessor, getProject()); 88 | for (String key : ymlProjectProcessor.getResult()) { 89 | lookupElements.add(LookupElementBuilder.create(key).withIcon(LaravelIcons.CONFIG)); 90 | } 91 | 92 | return lookupElements; 93 | } 94 | 95 | @NotNull 96 | @Override 97 | public Collection getPsiTargets(StringLiteralExpression element) { 98 | 99 | final Set targets = new HashSet<>(); 100 | 101 | final String contents = element.getContents(); 102 | if (StringUtils.isBlank(contents)) { 103 | return targets; 104 | } 105 | 106 | FileBasedIndex.getInstance().getFilesWithKey(ConfigKeyStubIndex.KEY, new HashSet<>(Collections.singletonList(contents)), new Processor() { 107 | @Override 108 | public boolean process(VirtualFile virtualFile) { 109 | PsiFile psiFileTarget = PsiManager.getInstance(ConfigKeyProvider.this.getProject()).findFile(virtualFile); 110 | if (psiFileTarget == null) { 111 | return true; 112 | } 113 | 114 | psiFileTarget.acceptChildren(new ArrayReturnPsiRecursiveVisitor( 115 | ConfigFileUtil.matchConfigFile(ConfigKeyProvider.this.getProject(), virtualFile).getKeyPrefix(), 116 | new ArrayKeyVisitor() { 117 | @Override 118 | public void visit(String key, PsiElement psiKey, boolean isRootElement) { 119 | if (!isRootElement && key.equals(contents)) { 120 | targets.add(psiKey); 121 | } 122 | } 123 | })); 124 | return true; 125 | } 126 | }, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(getProject()), PhpFileType.INSTANCE)); 127 | 128 | return targets; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/config/ArrayReturnPsiRecursiveVisitor.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.config; 2 | 3 | import pers.fw.tplugin.beans.ArrayKeyVisitor; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiRecursiveElementWalkingVisitor; 6 | import com.intellij.psi.util.PsiTreeUtil; 7 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 8 | import com.jetbrains.php.lang.psi.elements.ArrayHashElement; 9 | import com.jetbrains.php.lang.psi.elements.PhpReturn; 10 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 11 | import org.apache.commons.lang.StringUtils; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * @author Daniel Espendiller 19 | */ 20 | public class ArrayReturnPsiRecursiveVisitor extends PsiRecursiveElementWalkingVisitor { 21 | 22 | private final String fileNameWithoutExtension; 23 | private final ArrayKeyVisitor arrayKeyVisitor; 24 | 25 | public ArrayReturnPsiRecursiveVisitor(String fileNameWithoutExtension, ArrayKeyVisitor arrayKeyVisitor) { 26 | this.fileNameWithoutExtension = fileNameWithoutExtension; 27 | this.arrayKeyVisitor = arrayKeyVisitor; 28 | } 29 | 30 | @Override 31 | public void visitElement(PsiElement element) { 32 | 33 | // if (element instanceof PhpReturn) { 34 | // visitPhpReturn((PhpReturn) element); 35 | // } 36 | 37 | if (element instanceof ArrayCreationExpression && !(element.getParent().getParent() instanceof ArrayHashElement)) { //如果是创建数组会进行收录, 如果是数组中的数组不能满足条件 38 | // if (element instanceof ArrayCreationExpression) { 39 | collectConfigKeys((ArrayCreationExpression) element, this.arrayKeyVisitor, fileNameWithoutExtension); 40 | // Tool.printPsiTree(element.getParent().getParent()); 41 | // element = element.getNextSibling(); 42 | } 43 | 44 | super.visitElement(element); 45 | } 46 | 47 | public void visitPhpReturn(PhpReturn phpReturn) { 48 | PsiElement arrayCreation = phpReturn.getFirstPsiChild(); 49 | if (arrayCreation instanceof ArrayCreationExpression) { 50 | collectConfigKeys((ArrayCreationExpression) arrayCreation, this.arrayKeyVisitor, fileNameWithoutExtension); 51 | } 52 | } 53 | 54 | 55 | public static void collectConfigKeys(ArrayCreationExpression creationExpression, ArrayKeyVisitor arrayKeyVisitor, String configName) { 56 | collectConfigKeys(creationExpression, arrayKeyVisitor, Collections.singletonList(configName)); 57 | } 58 | 59 | public static void collectConfigKeys(ArrayCreationExpression creationExpression, ArrayKeyVisitor arrayKeyVisitor, List context) { 60 | 61 | List childrenOfTypeAsList = PsiTreeUtil.getChildrenOfTypeAsList(creationExpression, ArrayHashElement.class); 62 | for (ArrayHashElement hashElement : childrenOfTypeAsList) { //遍历文件所有元素 63 | 64 | PsiElement arrayKey = hashElement.getKey(); 65 | PsiElement arrayValue = hashElement.getValue(); 66 | 67 | if (arrayKey instanceof StringLiteralExpression) { //键是数组键 68 | 69 | List myContext = new ArrayList<>(context); 70 | 71 | //fwModify: 协助去掉文件前缀 72 | if (myContext.get(0).equals("")) 73 | myContext.remove(0); 74 | 75 | myContext.add(((StringLiteralExpression) arrayKey).getContents()); 76 | String keyName = StringUtils.join(myContext, "."); 77 | 78 | if (arrayValue instanceof ArrayCreationExpression) { 79 | arrayKeyVisitor.visit(keyName, arrayKey, false); //数组键也收录 80 | collectConfigKeys((ArrayCreationExpression) arrayValue, arrayKeyVisitor, myContext); 81 | } else { 82 | arrayKeyVisitor.visit(keyName, arrayKey, false); 83 | } 84 | } 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/config/CollectProjectUniqueKeys.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.config; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.psi.search.GlobalSearchScope; 5 | import com.intellij.util.Processor; 6 | import com.intellij.util.indexing.FileBasedIndex; 7 | import com.intellij.util.indexing.ID; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.Collection; 11 | import java.util.HashSet; 12 | import java.util.Set; 13 | 14 | /** 15 | * @author Daniel Espendiller 16 | */ 17 | public class CollectProjectUniqueKeys implements Processor { 18 | 19 | @NotNull 20 | private final Project project; 21 | 22 | @NotNull 23 | private final ID id; 24 | 25 | @NotNull 26 | private final Set stringSet; 27 | 28 | public CollectProjectUniqueKeys(@NotNull Project project, @NotNull ID id) { 29 | this.project = project; 30 | this.id = id; 31 | this.stringSet = new HashSet<>(); 32 | } 33 | 34 | @Override 35 | public boolean process(String s) { 36 | this.stringSet.add(s); 37 | return true; 38 | } 39 | 40 | public Set getResult() { 41 | Set set = new HashSet<>(); 42 | 43 | for (String key : stringSet) { 44 | Collection fileCollection = FileBasedIndex.getInstance().getContainingFiles(id, key, GlobalSearchScope.allScope(project)); 45 | if (fileCollection.size() > 0) { 46 | set.add(key); 47 | } 48 | } 49 | 50 | return set; 51 | } 52 | 53 | @NotNull 54 | public static Set collect(@NotNull Project project, @NotNull ID id) { 55 | CollectProjectUniqueKeys collector = new CollectProjectUniqueKeys(project, id); 56 | FileBasedIndex.getInstance().processAllKeys(id, collector, project); 57 | return collector.getResult(); 58 | } 59 | } -------------------------------------------------------------------------------- /src/pers/fw/tplugin/config/ConfigFileUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.config; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * 匹配文件 12 | */ 13 | public class ConfigFileUtil { 14 | private static final Pattern configFilePattern = Pattern.compile(".*/(config/([\\w-./]+)|application/([\\w-.]+)).php$"); 15 | private static final String[] configFiles = new String[]{"app", "cache", "cookie", "database", "log", "session", "template", "trace"}; 16 | 17 | public static ConfigFileMatchResult matchConfigFile(Project project, VirtualFile virtualFile) { 18 | String path = virtualFile.getPath(); 19 | String projectPath = project.getBaseDir().getPath(); 20 | 21 | if (path.startsWith(projectPath)) { 22 | path = path.substring(projectPath.length()); 23 | } 24 | Matcher m = configFilePattern.matcher(path); 25 | 26 | if (m.matches()) { 27 | // String temp0=m.group(0); 28 | // String temp1=m.group(1); 29 | // String temp2=m.group(2); 30 | // String temp3=m.group(3); 31 | String prefix2 = m.group(2); 32 | String prefix3 = m.group(3); 33 | if ((prefix2 != null && prefix2.contains("route")) || (prefix3 != null && prefix3.contains("route"))) 34 | return new ConfigFileMatchResult(false, ""); //忽略路由文件 35 | if ("database".equals(prefix3)) 36 | return new ConfigFileMatchResult(true, "database");//适配app目录下的配置文件 37 | if (m.group(0).startsWith("/config/")) { //适配5.1配置格式 38 | for (String configFile : configFiles) { 39 | if (configFile.equals(prefix2)) { 40 | return new ConfigFileMatchResult(true, configFile); 41 | } 42 | } 43 | } 44 | return new ConfigFileMatchResult(true, ""); 45 | } else { 46 | return new ConfigFileMatchResult(false, ""); 47 | } 48 | } 49 | 50 | public static class ConfigFileMatchResult { 51 | private boolean matches; 52 | 53 | private String keyPrefix; 54 | 55 | ConfigFileMatchResult(boolean matches, @NotNull String keyPrefix) { 56 | this.matches = matches; 57 | this.keyPrefix = keyPrefix; 58 | } 59 | 60 | public boolean matches() { 61 | return matches; 62 | } 63 | 64 | @NotNull 65 | public String getKeyPrefix() { 66 | return keyPrefix; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/config/ConfigKeyStubIndex.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.config; 2 | 3 | import pers.fw.tplugin.beans.ArrayKeyVisitor; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.util.indexing.*; 7 | import com.intellij.util.io.DataExternalizer; 8 | import com.intellij.util.io.EnumeratorStringDescriptor; 9 | import com.intellij.util.io.KeyDescriptor; 10 | import com.intellij.util.io.VoidDataExternalizer; 11 | import com.jetbrains.php.lang.PhpFileType; 12 | import com.jetbrains.php.lang.psi.PhpFile; 13 | import gnu.trove.THashMap; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.Map; 17 | 18 | public class ConfigKeyStubIndex extends FileBasedIndexExtension { 19 | 20 | public static final ID KEY = ID.create("fw.idea.thinkphp.config"); 21 | private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); 22 | 23 | @NotNull 24 | @Override 25 | public ID getName() { 26 | return KEY; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public DataIndexer getIndexer() { 32 | return new DataIndexer() { 33 | @NotNull 34 | @Override 35 | public Map map(@NotNull FileContent fileContent) { 36 | final Map map = new THashMap<>(); 37 | 38 | PsiFile psiFile = fileContent.getPsiFile(); 39 | if (!(psiFile instanceof PhpFile)) { 40 | return map; 41 | } 42 | 43 | //匹配文件 44 | ConfigFileUtil.ConfigFileMatchResult result = ConfigFileUtil.matchConfigFile(fileContent.getProject(), fileContent.getFile()); 45 | 46 | if (result.matches()) { 47 | psiFile.acceptChildren(new ArrayReturnPsiRecursiveVisitor(result.getKeyPrefix(), new ArrayKeyVisitor() { 48 | @Override 49 | public void visit(String key, PsiElement psiKey, boolean isRootElement) { 50 | if (!isRootElement) { 51 | map.put(key, null); 52 | } 53 | } 54 | })); 55 | } 56 | 57 | return map; 58 | } 59 | }; 60 | } 61 | 62 | @NotNull 63 | @Override 64 | public KeyDescriptor getKeyDescriptor() { 65 | return this.myKeyDescriptor; 66 | } 67 | 68 | @NotNull 69 | @Override 70 | public DataExternalizer getValueExternalizer() { 71 | return VoidDataExternalizer.INSTANCE; 72 | } 73 | 74 | @NotNull 75 | @Override 76 | public FileBasedIndex.InputFilter getInputFilter() { 77 | return file -> file.getFileType() == PhpFileType.INSTANCE; 78 | } 79 | 80 | @Override 81 | public boolean dependsOnFileContent() { 82 | return true; 83 | } 84 | 85 | @Override 86 | public int getVersion() { 87 | return 1; 88 | } 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/config/ProviderGotoCompletion.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.config; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 5 | import com.intellij.lang.Language; 6 | import com.intellij.patterns.PlatformPatterns; 7 | import com.intellij.psi.PsiElement; 8 | import com.jetbrains.php.PhpIndex; 9 | import com.jetbrains.php.lang.PhpLanguage; 10 | import com.jetbrains.php.lang.parser.PhpElementTypes; 11 | import com.jetbrains.php.lang.psi.elements.*; 12 | import pers.fw.tplugin.inter.GotoCompletionLanguageRegistrar; 13 | import pers.fw.tplugin.inter.GotoCompletionProvider; 14 | import pers.fw.tplugin.inter.GotoCompletionRegistrarParameter; 15 | import org.apache.commons.lang.StringUtils; 16 | import org.jetbrains.annotations.NotNull; 17 | import pers.fw.tplugin.util.PhpElementsUtil; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collection; 21 | import java.util.Collections; 22 | import java.util.stream.Collectors; 23 | 24 | /** 25 | * @author Daniel Espendiller 26 | */ 27 | public class ProviderGotoCompletion implements GotoCompletionLanguageRegistrar { 28 | 29 | @Override 30 | public void register(GotoCompletionRegistrarParameter registrar) { 31 | registrar.register(PlatformPatterns.psiElement(), psiElement -> { 32 | if (psiElement == null || !psiElement.getContainingFile().getName().contains("app.php")) { 33 | return null; 34 | } 35 | 36 | // array('providers' => array('foo')) 37 | PsiElement literal = psiElement.getParent(); 38 | if (literal instanceof StringLiteralExpression) { 39 | PsiElement arrayValue = literal.getParent(); 40 | if (arrayValue.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE) { 41 | PsiElement arrayCreation = arrayValue.getParent(); 42 | if (arrayCreation instanceof ArrayCreationExpression) { 43 | PsiElement arrayValueKey = arrayCreation.getParent(); 44 | if (arrayValueKey.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE) { 45 | PsiElement hashArrayElement = arrayValueKey.getParent(); 46 | if (hashArrayElement instanceof ArrayHashElement) { 47 | PhpPsiElement key = ((ArrayHashElement) hashArrayElement).getKey(); 48 | if (key instanceof StringLiteralExpression && "providers".equals(((StringLiteralExpression) key).getContents())) { 49 | return new ProviderName(psiElement); 50 | } 51 | 52 | } 53 | } 54 | } 55 | } 56 | } 57 | 58 | return null; 59 | }); 60 | 61 | } 62 | 63 | @Override 64 | public boolean support(@NotNull Language language) { 65 | return PhpLanguage.INSTANCE == language; 66 | } 67 | 68 | private class ProviderName extends GotoCompletionProvider { 69 | 70 | public ProviderName(PsiElement element) { 71 | super(element); 72 | } 73 | 74 | @NotNull 75 | @Override 76 | public Collection getLookupElements() { 77 | 78 | return PhpIndex.getInstance(getProject()).getAllSubclasses("\\Illuminate\\Support\\ServiceProvider") 79 | .stream() 80 | .map(phpClass -> LookupElementBuilder.create(phpClass.getPresentableFQN()) 81 | .withIcon(phpClass.getIcon())).collect(Collectors.toCollection(ArrayList::new) 82 | ); 83 | } 84 | 85 | @NotNull 86 | @Override 87 | public Collection getPsiTargets(StringLiteralExpression element) { 88 | 89 | String contents = element.getContents(); 90 | if (StringUtils.isBlank(contents)) { 91 | return Collections.emptyList(); 92 | } 93 | 94 | Collection psiElements = new ArrayList<>(); 95 | for (PhpClass phpClass : PhpElementsUtil.getClassesOrInterfaces(element.getProject(), contents)) { 96 | psiElements.add(phpClass); 97 | } 98 | 99 | return psiElements; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/db/Column.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.db; 2 | 3 | public class Column { 4 | public Column(String comment, String table) { 5 | this.comment = comment; 6 | this.table = table; 7 | } 8 | 9 | public String comment=""; 10 | public String table=""; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/db/DbTableUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.db; 2 | 3 | import com.jetbrains.php.lang.psi.elements.MethodReference; 4 | import com.jetbrains.php.lang.psi.elements.PhpClass; 5 | import com.jetbrains.php.lang.psi.elements.impl.PhpClassImpl; 6 | import pers.fw.tplugin.beans.ArrayMapVisitor; 7 | import com.intellij.database.model.DasColumn; 8 | import com.intellij.database.model.DasTable; 9 | import com.intellij.database.psi.DbDataSource; 10 | import com.intellij.database.util.DasUtil; 11 | import com.intellij.database.util.DbUtil; 12 | import com.intellij.openapi.project.Project; 13 | import com.intellij.psi.PsiElement; 14 | import com.intellij.util.containers.JBIterable; 15 | import com.jetbrains.php.lang.psi.elements.Method; 16 | import pers.fw.tplugin.util.*; 17 | 18 | import java.util.*; 19 | 20 | public class DbTableUtil { 21 | 22 | public static String[] getPossibleTables() { 23 | 24 | 25 | return null; 26 | } 27 | 28 | //根据模型表获取列, 29 | public static JBIterable getColumns(Project project, String table, int type) { 30 | if (table.isEmpty()) return null; 31 | table = table.replace("'", "").replace("\"", ""); 32 | JBIterable tables = getTables(project); 33 | if (tables == null) return null; 34 | if (type == 1) { //有前缀 35 | for (DasTable item : tables) { 36 | String tableName = item.getName(); 37 | int i = item.getName().indexOf("_"); 38 | if (i == -1) { 39 | if (tableName.equals(table)) { 40 | return DasUtil.getColumns(item); 41 | } 42 | } else { 43 | String name = tableName.substring(i + 1, tableName.length()); 44 | if (name.equals(table)) { 45 | return DasUtil.getColumns(item); 46 | } 47 | } 48 | } 49 | } else if (type == 2) { //无前缀 50 | for (DasTable item : tables) { 51 | if (item.getName().equals(table)) { 52 | return DasUtil.getColumns(item); 53 | } 54 | } 55 | } 56 | return null; 57 | } 58 | 59 | public static JBIterable getColumns(Project project, String table) { 60 | if (table == null) return null; 61 | try { 62 | JBIterable tables = getTables(project); 63 | for (DasTable item : tables) { 64 | if (table.equals(item.getName())) { 65 | return DasUtil.getColumns(item); 66 | } 67 | } 68 | } catch (Exception e) { 69 | return null; 70 | } 71 | return null; 72 | } 73 | 74 | //获取所有表 75 | public static JBIterable getTables(Project project) { 76 | 77 | JBIterable dataSources = DbUtil.getDataSources(project); 78 | if (dataSources.size() < 1) { 79 | return null; 80 | } else { 81 | DbDataSource work = null; 82 | for (DbDataSource db : dataSources) { 83 | if (db.getName().contains("work")) { 84 | work = db; 85 | break; 86 | } 87 | } 88 | if (work != null) { 89 | return DasUtil.getTables(work.getDelegate()); 90 | } else { 91 | if (dataSources.get(0) == null) return null; 92 | DbDataSource dbDataSource = dataSources.get(0); 93 | if(dbDataSource==null)return null; 94 | return DasUtil.getTables(dbDataSource.getDelegate()); 95 | } 96 | } 97 | } 98 | 99 | // public static String getTableName(PsiElement element) { 100 | // PsiElement parent = element.getParent(); 101 | // if (parent == null) return null; 102 | // MethodReferenceImpl parent1 = (MethodReferenceImpl) parent.getParent(); 103 | // if (parent1 == null) return null; 104 | // parent1.getClassReference(); 105 | // return ""; 106 | // } 107 | 108 | // private static final Pattern aliasPattern = Pattern.compile(".*/application/(\\w+)/controller/(\\w+).php$"); 109 | //从上下文中获取表 110 | public static void collectionTableByContext(PsiElement element, HashSet tables) { 111 | Map alias = new HashMap<>(); 112 | Method method = Util.getMethod(element); 113 | if (method == null) return; 114 | method.acceptChildren(new TablesVisitor(new ArrayMapVisitor() { 115 | @Override 116 | public void visit(String key, String value) { 117 | tables.add(key); 118 | } 119 | }, tables)); 120 | return; 121 | } 122 | 123 | public static void collectionTableByCurFile(PsiElement psiElement, HashSet tables) { 124 | 125 | PhpClassImpl phpClass = Util.getPhpClass(psiElement); 126 | String table=Util.getTableByClass(phpClass, psiElement.getProject()); 127 | if(table!=null){ 128 | tables.add(table); 129 | } 130 | } 131 | 132 | //从模型变量中获取表 133 | public static void collectionTableByModel(PsiElement psiElement, HashSet tables) { 134 | //获取方法对象的类 135 | // PsiElement resolve = ((MethodReference) methodRef).resolve(); 136 | //Query类 137 | // PhpClassImpl phpClass = Util.getPhpClass(resolve); 138 | //获取可能出现的表 139 | // List tables= new ArrayList<>(); 140 | // Tables tables = new Tables(); 141 | PsiElement paramList = psiElement.getParent(); 142 | if (paramList == null) return; 143 | PsiElement methodRef = paramList.getParent(); 144 | if (!(methodRef instanceof MethodReference)) return; 145 | 146 | Project project = psiElement.getProject(); 147 | //Model子类 148 | PhpClass phpClass = Util.getInstanseClass(project, (MethodReference) methodRef); //获取模型类 149 | String table = Util.getTableByClass(phpClass, project); 150 | if(table!=null){ 151 | tables.add(table); 152 | } 153 | // if (phpClass != null) { 154 | // Collection fields = phpClass.getFields(); 155 | // for (Field item : fields) { 156 | // if ("name".equals(item.getName())) { 157 | // String name = item.getDefaultValue().getText(); 158 | // if (name != null && !name.isEmpty() && !"$name".equals(name)) { 159 | // tables.add(DbTableUtil.getTableByName(project, name)); 160 | // break; 161 | // } 162 | // } 163 | // if ("table".equals(item.getName())) { 164 | // String name = item.getDefaultValue().getText();//item.getDefaultValuePresentation(); 165 | // if (name != null && !name.isEmpty() && !"$table".equals(name)) { 166 | // name = name.replace("'", "").replace("\"", ""); 167 | // tables.add(name); 168 | // break; 169 | // } 170 | // } 171 | // } 172 | // } 173 | } 174 | 175 | 176 | public static String getTableByName(Project project, String name) { 177 | if (name.isEmpty()) return null; 178 | name = name.replace("'", "").replace("\"", ""); 179 | JBIterable tables = getTables(project); 180 | if (tables == null) return null; 181 | for (DasTable item : tables) { 182 | String tableName = item.getName(); 183 | if (tableName.contains(name)) { 184 | String prefix = tableName.replace(name, ""); 185 | if (prefix.equals(tableName.substring(0, prefix.length()))) { 186 | if (prefix.indexOf("_") == prefix.length() - 1) 187 | return tableName; 188 | } 189 | } 190 | } 191 | return null; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/db/TableBean.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.db; 2 | 3 | public class TableBean { 4 | 5 | public String table; 6 | public String alias; 7 | 8 | public TableBean(String table, String alias) { 9 | this.table = table; 10 | this.alias = alias; 11 | } 12 | 13 | public TableBean(String table) { 14 | this.table = table; 15 | } 16 | 17 | public String getTable() { 18 | return table; 19 | } 20 | 21 | public void setTable(String table) { 22 | this.table = table; 23 | } 24 | 25 | public String getAlias() { 26 | return alias; 27 | } 28 | 29 | public void setAlias(String alias) { 30 | this.alias = alias; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/db/Tables.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.db; 2 | 3 | 4 | import java.util.HashMap; 5 | import java.util.HashSet; 6 | import java.util.Map; 7 | 8 | public class Tables { 9 | 10 | 11 | public Map> getTables() { 12 | return tables; 13 | } 14 | 15 | private Map> tables; 16 | 17 | public Tables() { 18 | this.tables = new HashMap<>(); 19 | } 20 | 21 | public void put(String table) { 22 | this.put(table, null); 23 | } 24 | 25 | public void put(String table, String alias) { 26 | if (tables.containsKey(table)) { 27 | HashSet aliases = tables.get(table); 28 | if (alias != null) 29 | aliases.add(alias); 30 | } else { 31 | HashSet aliases = new HashSet<>(); 32 | if (alias != null) 33 | aliases.add(alias); 34 | tables.put(table, aliases); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/db/TablesVisitor.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.db; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.psi.PsiReference; 5 | import com.jetbrains.php.PhpIndex; 6 | import com.jetbrains.php.lang.psi.elements.FunctionReference; 7 | import com.jetbrains.php.lang.psi.elements.PhpClass; 8 | import com.jetbrains.php.lang.psi.elements.impl.VariableImpl; 9 | import pers.fw.tplugin.beans.ArrayMapVisitor; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiRecursiveElementWalkingVisitor; 12 | import com.jetbrains.php.lang.psi.elements.impl.ParameterListImpl; 13 | import pers.fw.tplugin.util.MethodMatcher; 14 | import pers.fw.tplugin.util.PsiElementUtil; 15 | import pers.fw.tplugin.util.Util; 16 | 17 | import java.util.Collection; 18 | import java.util.HashSet; 19 | import java.util.Set; 20 | 21 | public class TablesVisitor extends PsiRecursiveElementWalkingVisitor { 22 | private final ArrayMapVisitor visitor; 23 | private HashSet tables; 24 | 25 | public TablesVisitor(ArrayMapVisitor visitor, HashSet tables) { 26 | this.visitor = visitor; 27 | this.tables = tables; 28 | } 29 | 30 | private static MethodMatcher.CallToSignature[] alias = new MethodMatcher.CallToSignature[]{ 31 | new MethodMatcher.CallToSignature("\\think\\db\\Query", "alias")}; 32 | 33 | private static MethodMatcher.CallToSignature[] join = new MethodMatcher.CallToSignature[]{ 34 | new MethodMatcher.CallToSignature("\\think\\db\\Query", "join")}; 35 | 36 | private static MethodMatcher.CallToSignature[] table = new MethodMatcher.CallToSignature[]{ 37 | new MethodMatcher.CallToSignature("\\think\\db\\Query", "table"), 38 | new MethodMatcher.CallToSignature("\\think\\Db", "table"), 39 | }; 40 | 41 | private void addTable(PsiElement param, int type) { 42 | String text = param.getText().replace("'", "").replace("\"", ""); 43 | if (type == 1) { 44 | text = DbTableUtil.getTableByName(param.getProject(), text); 45 | } 46 | this.visitor.visit(text, null); 47 | } 48 | 49 | @Override 50 | public void visitElement(PsiElement element) { 51 | if (element instanceof VariableImpl) { //从模型变量收集 52 | PsiReference reference = element.getReference(); 53 | if (reference == null) { 54 | super.visitElement(element); 55 | } else { 56 | Set types = ((VariableImpl) reference).getType().getTypes(); 57 | Project project = element.getProject(); 58 | for (String item : types) { 59 | if (item.contains("\\model\\")) { //model子类 60 | Collection classesByFQN = PhpIndex.getInstance(project).getClassesByFQN(item); 61 | for (PhpClass cls : classesByFQN) { 62 | String table = Util.getTableByClass(cls, project); 63 | this.visitor.visit(table, null); 64 | } 65 | } 66 | } 67 | } 68 | } else if (element instanceof FunctionReference) { //从table, join, db, name 收集 69 | PsiElement[] childrens = element.getChildren(); 70 | for (PsiElement paramList : childrens) { 71 | if (paramList instanceof ParameterListImpl) { 72 | if (paramList.getChildren().length > 0) { 73 | PsiElement param = paramList.getChildren()[0]; 74 | // String methodName = ((FunctionReference) element).getName(); 75 | if (PsiElementUtil.isFunctionReference(param, "join", 0)) { 76 | String text = param.getText().replace("'", "").replace("\"", ""); 77 | String[] s = text.split(" "); 78 | this.visitor.visit(s[0], null); 79 | } else if (PsiElementUtil.isFunctionReference(param, "table", 0)) { 80 | addTable(param, 0); 81 | } else if (PsiElementUtil.isFunctionReference(param, "db", 0) || PsiElementUtil.isFunctionReference(param, "name", 0)) { 82 | addTable(param, 1); 83 | } 84 | } 85 | } else if (paramList instanceof FunctionReference) { //链式调用方法 86 | super.visitElement(element); 87 | } 88 | } 89 | } else { 90 | super.visitElement(element); 91 | } 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/inter/CompletionContributorParameter.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.inter; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionResultSet; 5 | import com.intellij.util.ProcessingContext; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | /** 9 | * @author Daniel Espendiller 10 | */ 11 | public class CompletionContributorParameter { 12 | @NotNull 13 | private final CompletionParameters completionParameters; 14 | 15 | @NotNull 16 | private final ProcessingContext processingContext; 17 | 18 | @NotNull 19 | private final CompletionResultSet completionResultSet; 20 | 21 | public CompletionContributorParameter(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { 22 | this.completionParameters = completionParameters; 23 | this.processingContext = processingContext; 24 | this.completionResultSet = completionResultSet; 25 | } 26 | 27 | @NotNull 28 | public CompletionParameters getCompletionParameters() { 29 | return completionParameters; 30 | } 31 | 32 | @NotNull 33 | public ProcessingContext getProcessingContext() { 34 | return processingContext; 35 | } 36 | 37 | @NotNull 38 | public CompletionResultSet getCompletionResultSet() { 39 | return completionResultSet; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/inter/GotoCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.inter; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public interface GotoCompletionContributor { 7 | @Nullable 8 | GotoCompletionProvider getProvider(@Nullable PsiElement psiElement); 9 | } 10 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/inter/GotoCompletionLanguageRegistrar.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.inter; 2 | 3 | import com.intellij.lang.Language; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * @author Daniel Espendiller 8 | */ 9 | public interface GotoCompletionLanguageRegistrar extends GotoCompletionRegistrar { 10 | boolean support(@NotNull Language language); 11 | } 12 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/inter/GotoCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.inter; 2 | 3 | import com.intellij.openapi.editor.Editor; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.psi.PsiElement; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.net.URISyntaxException; 9 | import java.util.Collection; 10 | import java.util.Collections; 11 | 12 | public abstract class GotoCompletionProvider implements GotoCompletionProviderInterface { 13 | 14 | private final PsiElement element; 15 | 16 | public GotoCompletionProvider(PsiElement element) { 17 | this.element = element; 18 | } 19 | 20 | protected Project getProject() { 21 | return this.element.getProject(); 22 | } 23 | 24 | protected PsiElement getElement() { 25 | return this.element; 26 | } 27 | 28 | @NotNull 29 | public Collection getPsiTargets(PsiElement element) { 30 | return Collections.emptyList(); 31 | } 32 | 33 | public void getLookupElements(CompletionContributorParameter parameter) { 34 | } 35 | 36 | @NotNull 37 | public Collection getPsiTargets(@NotNull PsiElement psiElement, int offset, @NotNull Editor editor){ 38 | return Collections.emptyList(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/inter/GotoCompletionProviderInterface.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.inter; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.psi.PsiElement; 6 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.net.URISyntaxException; 10 | import java.util.Collection; 11 | import java.util.Collections; 12 | 13 | public interface GotoCompletionProviderInterface { 14 | void getLookupElements(CompletionContributorParameter parameter); 15 | 16 | @NotNull 17 | Collection getLookupElements(); 18 | 19 | @NotNull 20 | default Collection getPsiTargets(StringLiteralExpression element) { 21 | return Collections.emptyList(); 22 | } 23 | 24 | @NotNull 25 | Collection getPsiTargets(PsiElement element); 26 | 27 | @NotNull 28 | Collection getPsiTargets(@NotNull PsiElement psiElement, int offset, @NotNull Editor editor); 29 | } 30 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/inter/GotoCompletionRegistrar.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.inter; 2 | 3 | public interface GotoCompletionRegistrar { 4 | void register(GotoCompletionRegistrarParameter registrar); 5 | } 6 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/inter/GotoCompletionRegistrarParameter.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.inter; 2 | 3 | import com.intellij.patterns.ElementPattern; 4 | import com.intellij.psi.PsiElement; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public interface GotoCompletionRegistrarParameter { 8 | public void register(@NotNull ElementPattern pattern, GotoCompletionContributor contributor); 9 | } 10 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/log/LogUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.log; 2 | 3 | public class LogUtil { 4 | public static boolean isLogFile(String filename) { 5 | String ext = filename.substring(filename.lastIndexOf(".") + 1, filename.length()); 6 | if ("log".equals(ext)) 7 | return true; 8 | else 9 | return false; 10 | } 11 | 12 | public static String getTimeStr(String str) { 13 | //处理时间行: 14 | 15 | if (str.startsWith("[ 2")) { 16 | int start = str.indexOf("["); 17 | int end = str.indexOf("]"); 18 | String timeStr = str.substring(start + 2, end); 19 | if (timeStr != null) { 20 | timeStr = timeStr.trim(); 21 | //e: 2018-07-18T12:47:03 22 | timeStr = timeStr.substring(0, 19); 23 | timeStr = timeStr.replace("T", " "); 24 | return timeStr; 25 | } 26 | } 27 | return null; 28 | } 29 | 30 | // 获取类型1; 31 | public static String[] getContent(String str) { 32 | String[] arr = new String[3]; 33 | String type = ""; 34 | int l = str.indexOf("["); 35 | int r = str.indexOf("]"); 36 | if (l != -1 && r != -1 && r > 1) 37 | type = str.substring(l + 1, r); 38 | 39 | String type2 = ""; 40 | int l1 = str.indexOf("] ["); 41 | int r1 = str.substring(l1 + 3).indexOf("]") + l1 + 3; 42 | if (l1 != -1 && r1 != -1 && r1 > l1) 43 | type2 = str.substring(l1 + 3, r1); 44 | 45 | String content = ""; 46 | if (r1 != -1 && l1 != -1) 47 | content = str.substring(r1 + 1); 48 | else if (r != -1 && r < 5) 49 | content = str.substring(r + 1); 50 | else 51 | content = str; 52 | 53 | arr[0] = type.trim(); 54 | arr[1] = type2.trim(); 55 | arr[2] = content; 56 | return arr; 57 | } 58 | 59 | // 获取类型2 60 | public static String getType2(String str) { 61 | String type2 = ""; 62 | int l1 = str.indexOf("] ["); 63 | if (l1 != -1) return type2; 64 | int r1 = str.substring(l1 + 3).indexOf("]"); 65 | if (r1 == -1) return type2; 66 | type2 = str.substring(l1 + 3, r1); 67 | String content = ""; 68 | content = str.substring(r1 + 1); 69 | return type2; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/log/MyToolWindow.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 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/log/MyToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.log; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.*; 5 | import com.intellij.openapi.wm.ToolWindow; 6 | import com.intellij.openapi.wm.ToolWindowFactory; 7 | import com.intellij.ui.content.Content; 8 | import com.intellij.ui.content.ContentFactory; 9 | import org.jetbrains.annotations.NotNull; 10 | import pers.fw.tplugin.beans.Setting; 11 | import pers.fw.tplugin.util.Util; 12 | 13 | import javax.swing.*; 14 | import javax.swing.event.ListSelectionEvent; 15 | import javax.swing.event.ListSelectionListener; 16 | import java.awt.*; 17 | import java.io.BufferedReader; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.io.InputStreamReader; 21 | import java.text.SimpleDateFormat; 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | import java.util.Vector; 26 | import java.util.regex.Matcher; 27 | import java.util.regex.Pattern; 28 | 29 | /** 30 | * Created by IntelliJ IDEA. 31 | * User: Alexey.Chursin 32 | * Date: Aug 25, 2010 33 | * Time: 2:09:00 PM 34 | */ 35 | public class MyToolWindowFactory implements ToolWindowFactory { 36 | 37 | private JButton refreshToolWindowButton; 38 | private JButton hideToolWindowButton; 39 | private JLabel currentDate; 40 | private JLabel currentTime; 41 | private JLabel timeZone; 42 | private JPanel myToolWindowContent; 43 | private JList list1; 44 | // private JList list2; 45 | private JTextPane textPane1; 46 | private JScrollPane scroll1; 47 | private ToolWindow myToolWindow; 48 | private static String oldTime = "1000"; 49 | 50 | public MyToolWindowFactory() { 51 | // hideToolWindowButton.addActionListener(new ActionListener() { 52 | // public void actionPerformed(ActionEvent e) { 53 | // myToolWindow.hide(null); 54 | // } 55 | // }); 56 | // refreshToolWindowButton.addActionListener(new ActionListener() { 57 | // public void actionPerformed(ActionEvent e) { 58 | // MyToolWindowFactory.this.currentDateTime(); 59 | // } 60 | // }); 61 | // return; 62 | init(); 63 | } 64 | 65 | // Create the tool window content. 66 | public void createToolWindowContent(Project project, ToolWindow toolWindow) { 67 | myToolWindow = toolWindow; 68 | ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); 69 | Content content = contentFactory.createContent(myToolWindowContent, "", false); 70 | toolWindow.getContentManager().addContent(content); 71 | } 72 | 73 | public void init() { 74 | String date = new SimpleDateFormat("[ yyyy-MM-dd").format(new Date()); 75 | String time = new SimpleDateFormat("HH:mm:ss").format(new Date()); 76 | 77 | oldTime = date + "T" + time; 78 | 79 | Vector records = new Vector(); 80 | list1.addListSelectionListener(new ListSelectionListener() { 81 | @Override 82 | public void valueChanged(ListSelectionEvent e) { 83 | String value = (String) list1.getSelectedValue(); 84 | value = Util.formatLog(value); 85 | textPane1.setText(value); 86 | } 87 | }); 88 | 89 | VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() { 90 | @Override 91 | public void contentsChanged(@NotNull VirtualFileEvent event) { 92 | //判断文件是否是日志文件 93 | String fileName = event.getFileName(); 94 | if (fileName.equals(Setting.fileName)) { 95 | Util.setConfig(event.getFile().getPath()); 96 | return; 97 | } 98 | if (!LogUtil.isLogFile(fileName)) 99 | return; 100 | VirtualFile file = event.getFile(); 101 | try { 102 | InputStream inputStream = file.getInputStream(); 103 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, CharsetToolkit.UTF8)); 104 | String line = ""; 105 | List tempList = new ArrayList(); 106 | boolean append = true; 107 | String[] logPrefix = Setting.config.logPrefix; 108 | String[] logRegex = Setting.config.logRegex; 109 | List patterns=new ArrayList(); 110 | for(String item : logRegex){ 111 | Pattern p = Pattern.compile(item); 112 | patterns.add(p); 113 | } 114 | 115 | while ((line = reader.readLine()) != null) { 116 | if (line.startsWith("[ 2")) { 117 | if (line.compareTo(oldTime) > 0) { 118 | oldTime = line; 119 | tempList.add(line); //开始记录的时间行 120 | 121 | while ((line = reader.readLine()) != null) { 122 | if (line.startsWith("-")) continue; //忽略 123 | if (line.startsWith("[")) { 124 | if (line.startsWith("[ 2")) { 125 | // 记录,更新时间 126 | tempList.add(line); 127 | oldTime = line; 128 | } else { 129 | if (logPrefix != null && logPrefix.length != 0) { //判断前缀 130 | for (String item : logPrefix) { 131 | append = false; 132 | if (line.startsWith(item)) { 133 | append = true; 134 | tempList.add(" "+line); 135 | break; 136 | } 137 | } 138 | } else if(logRegex.length!=0){ //判断正则 139 | for (Pattern item : patterns) { 140 | append = false; 141 | Matcher matcher = item.matcher(line); 142 | if (matcher.matches()) { 143 | append = true; 144 | tempList.add(" "+line); 145 | break; 146 | } 147 | } 148 | }else{ 149 | tempList.add(" "+line); 150 | append=true; 151 | } 152 | } 153 | } else { 154 | // 追加内容, 多行一条记录 155 | if (!append||tempList.size() == 0) continue; 156 | String str = (String) tempList.get(tempList.size() - 1); 157 | str = str + line; 158 | tempList.set(tempList.size() - 1, str); 159 | } 160 | } 161 | } 162 | } 163 | } 164 | records.addAll(tempList); 165 | } catch (IOException e) { 166 | e.printStackTrace(); 167 | } 168 | list1.setListData(records); 169 | // list1.updateUI(); 170 | int height = list1.getHeight(); 171 | scroll1.getViewport().setViewPosition(new Point(0, height)); 172 | } 173 | }); 174 | } 175 | } -------------------------------------------------------------------------------- /src/pers/fw/tplugin/log/ShowLog.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.log; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.project.ProjectManager; 5 | import com.intellij.openapi.util.Condition; 6 | import org.jetbrains.annotations.NotNull; 7 | import pers.fw.tplugin.beans.Setting; 8 | import pers.fw.tplugin.util.Util; 9 | 10 | public class ShowLog implements Condition { 11 | 12 | @Override 13 | public boolean value(Object o) { 14 | @NotNull Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); 15 | if (openProjects.length == 0) { 16 | return false; 17 | } 18 | Project project = ProjectManager.getInstance().getOpenProjects()[0]; 19 | String root = project.getBasePath(); 20 | Util.setConfig(root + "/" + Setting.fileName); 21 | return Setting.config.logEnable; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/main/Completer.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.main; 2 | 3 | import com.intellij.codeInsight.completion.*; 4 | import com.intellij.patterns.PlatformPatterns; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.util.ProcessingContext; 7 | import pers.fw.tplugin.db.DbReference; 8 | import pers.fw.tplugin.inter.CompletionContributorParameter; 9 | import pers.fw.tplugin.inter.GotoCompletionContributor; 10 | import pers.fw.tplugin.inter.GotoCompletionProviderInterface; 11 | import org.jetbrains.annotations.NotNull; 12 | import pers.fw.tplugin.util.GotoCompletionUtil; 13 | import pers.fw.tplugin.util.Util; 14 | 15 | //代码补全主入口 16 | public class Completer extends CompletionContributor { 17 | 18 | public Completer() { 19 | 20 | try { 21 | extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new CompletionProvider() { 22 | @Override 23 | protected void addCompletions(@NotNull CompletionParameters completionParameters, ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { 24 | 25 | PsiElement psiElement = completionParameters.getOriginalPosition(); 26 | if (psiElement == null) { // || !LaravelProjectComponent.isEnabled(psiElement)) { 27 | return; 28 | } 29 | CompletionContributorParameter parameter = null; 30 | for (GotoCompletionContributor contributor : GotoCompletionUtil.getContributors(psiElement)) { 31 | GotoCompletionProviderInterface formReferenceCompletionContributor = contributor.getProvider(psiElement); 32 | if (formReferenceCompletionContributor != null) { 33 | if (formReferenceCompletionContributor instanceof DbReference.ColumnProvider) { //数据库列重定义前缀 34 | PrefixMatcher prefixMatcher = completionResultSet.getPrefixMatcher(); 35 | String rePrefix = Util.rePrefix(prefixMatcher.getPrefix()); 36 | completionResultSet.withPrefixMatcher(prefixMatcher.cloneWithPrefix(rePrefix)) 37 | .addAllElements(formReferenceCompletionContributor.getLookupElements()); 38 | } else { 39 | completionResultSet.addAllElements(formReferenceCompletionContributor.getLookupElements()); 40 | } 41 | if (parameter == null) { 42 | parameter = new CompletionContributorParameter(completionParameters, processingContext, completionResultSet); 43 | } 44 | formReferenceCompletionContributor.getLookupElements(parameter); 45 | } 46 | } 47 | } 48 | }); 49 | } catch (Exception e) { 50 | //不弹出错误 51 | } 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/main/Documenter.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.main; 2 | 3 | import com.intellij.lang.documentation.AbstractDocumentationProvider; 4 | import com.intellij.lang.documentation.DocumentationProvider; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiManager; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.List; 10 | 11 | public class Documenter implements DocumentationProvider { 12 | 13 | @Nullable 14 | @Override 15 | public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { 16 | return "fffff"; 17 | } 18 | 19 | @Nullable 20 | @Override 21 | public List getUrlFor(PsiElement element, PsiElement originalElement) { 22 | return null; 23 | } 24 | 25 | @Nullable 26 | @Override 27 | public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { 28 | return ""; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { 34 | return null; 35 | } 36 | 37 | @Nullable 38 | @Override 39 | public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/main/GotoHandler.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.main; 2 | 3 | import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; 4 | import com.intellij.openapi.actionSystem.DataContext; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.psi.PsiElement; 7 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 8 | import pers.fw.tplugin.db.DbReference; 9 | import pers.fw.tplugin.inter.GotoCompletionContributor; 10 | import pers.fw.tplugin.inter.GotoCompletionProviderInterface; 11 | import org.jetbrains.annotations.Nullable; 12 | import pers.fw.tplugin.util.GotoCompletionUtil; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collection; 16 | //跳转主入口 17 | public class GotoHandler implements GotoDeclarationHandler { 18 | @Nullable 19 | @Override 20 | public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor) { 21 | 22 | //fwmodify:关闭开关 23 | // if (!LaravelProjectComponent.isEnabled(psiElement)) { 24 | // return new PsiElement[0];} 25 | Collection psiTargets = new ArrayList(); 26 | try { 27 | if (psiElement == null) return psiTargets.toArray(new PsiElement[psiTargets.size()]); //报错解决 28 | 29 | PsiElement parent = psiElement.getParent(); 30 | for (GotoCompletionContributor contributor : GotoCompletionUtil.getContributors(psiElement)) { 31 | GotoCompletionProviderInterface gotoCompletionContributorProvider = contributor.getProvider(psiElement); 32 | if (gotoCompletionContributorProvider != null) { 33 | if (parent instanceof StringLiteralExpression) { 34 | psiTargets.addAll(gotoCompletionContributorProvider.getPsiTargets((StringLiteralExpression) parent)); 35 | } else { 36 | psiTargets.addAll(gotoCompletionContributorProvider.getPsiTargets(psiElement)); 37 | } 38 | 39 | psiTargets.addAll(gotoCompletionContributorProvider.getPsiTargets(psiElement, i, editor)); 40 | } 41 | } 42 | }catch (Exception e){ 43 | // 44 | } 45 | return psiTargets.toArray(new PsiElement[psiTargets.size()]); 46 | } 47 | 48 | @Nullable 49 | @Override 50 | public String getActionText(DataContext dataContext) { 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/main/MyTypeProvider.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.main; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.php.lang.psi.elements.*; 6 | import com.jetbrains.php.lang.psi.resolve.types.PhpType; 7 | import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider3; 8 | import org.jetbrains.annotations.Nullable; 9 | import pers.fw.tplugin.util.Util; 10 | 11 | import java.util.Collection; 12 | import java.util.Set; 13 | 14 | public class MyTypeProvider implements PhpTypeProvider3 { 15 | // private static final Key>>> MODEL_TYPE_MAP = 16 | // new Key>>>("MODEL_TYPE_MAP"); 17 | 18 | @Override 19 | public char getKey() { 20 | return 'F'; 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public PhpType getType(PsiElement psiElement) { 26 | try { 27 | if (psiElement instanceof FunctionReference && "model".equals(((FunctionReference) psiElement).getName())) { 28 | FunctionReference fun = (FunctionReference) psiElement; 29 | ParameterList parameterList = fun.getParameterList(); 30 | if (parameterList != null) { 31 | PsiElement[] parameters = parameterList.getParameters(); 32 | if (parameters.length > 0) { 33 | String layer="model"; 34 | if(parameters.length>1){ 35 | layer = parameters[1].getText().replace("'", "").replace("\"", ""); 36 | } 37 | String moduleName = ""; 38 | String text = parameters[0].getText().replace("'", "").replace("\"", ""); 39 | if (text.contains("/")) { //跨模块的model 40 | String[] split = text.split("/"); 41 | moduleName = split[0]; 42 | text= ""; 43 | if (split.length < 2) return null; 44 | boolean flag=false; 45 | for(int i=1;i getPsiTargets(StringLiteralExpression psiElement) { 67 | //// return super.getPsiTargets(psiElement, offset, editor); 68 | // final Set targets = new HashSet<>(); 69 | // 70 | // String contents = psiElement.getContents(); 71 | // if (StringUtils.isBlank(contents)) { 72 | // return targets; 73 | // } 74 | // if (!contents.contains("/")) contents = Util.getCurTpModuleName(getElement()) + "/" + contents; 75 | // FileBasedIndex.getInstance().getFilesWithKey(ModelStubIndex.KEY, new HashSet<>(Collections.singletonList(contents)), 76 | // new Processor() { 77 | // @Override 78 | // public boolean process(VirtualFile virtualFile) { 79 | // if (virtualFile != null) { 80 | // PsiFile psiFileTarget = PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile); 81 | // targets.add(psiFileTarget); 82 | // } 83 | // return true; 84 | // } 85 | // }, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(psiElement.getProject()), PhpFileType.INSTANCE)); 86 | // 87 | // return targets; 88 | // } 89 | 90 | @Override 91 | public Collection getBySignature(String s, Set set, int i, Project project) { 92 | return null; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/main/Templater.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.main; 2 | 3 | import com.intellij.codeInsight.template.impl.DefaultLiveTemplatesProvider; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class Templater implements DefaultLiveTemplatesProvider { 7 | @Override 8 | public String[] getDefaultLiveTemplateFiles() { 9 | return new String[]{ 10 | "liveTemplates/ThinkPHP-5" 11 | }; 12 | } 13 | 14 | @Nullable 15 | @Override 16 | public String[] getHiddenLiveTemplateFiles() { 17 | return new String[0]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/model/ControllerStubIndex.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.model; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.intellij.util.indexing.*; 5 | import com.intellij.util.io.DataExternalizer; 6 | import com.intellij.util.io.EnumeratorStringDescriptor; 7 | import com.intellij.util.io.KeyDescriptor; 8 | import com.intellij.util.io.VoidDataExternalizer; 9 | import com.jetbrains.php.lang.PhpFileType; 10 | import com.jetbrains.php.lang.psi.PhpFile; 11 | import gnu.trove.THashMap; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Map; 15 | 16 | public class ControllerStubIndex extends FileBasedIndexExtension { 17 | 18 | public static final ID KEY = ID.create("fw.idea.thinkphp.controller"); 19 | 20 | private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); 21 | 22 | @NotNull 23 | @Override 24 | public ID getName() { 25 | return KEY; 26 | } 27 | 28 | @NotNull 29 | @Override 30 | public DataIndexer getIndexer() { 31 | return new DataIndexer() { 32 | @NotNull 33 | @Override 34 | public Map map(@NotNull FileContent fileContent) { 35 | final Map map = new THashMap<>(); 36 | 37 | PsiFile psiFile = fileContent.getPsiFile(); 38 | if (!(psiFile instanceof PhpFile)) { 39 | return map; 40 | } 41 | psiFile.getName(); 42 | //匹配文件 43 | String model = ModelUtil.matchControllerFile(fileContent.getProject(), fileContent.getFile()); 44 | if (model != null) 45 | map.put(model, null); 46 | return map; 47 | } 48 | }; 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public KeyDescriptor getKeyDescriptor() { 54 | return this.myKeyDescriptor; 55 | } 56 | 57 | @NotNull 58 | @Override 59 | public DataExternalizer getValueExternalizer() { 60 | return VoidDataExternalizer.INSTANCE; 61 | } 62 | 63 | @NotNull 64 | @Override 65 | public FileBasedIndex.InputFilter getInputFilter() { 66 | return file -> file.getFileType() == PhpFileType.INSTANCE; 67 | } 68 | 69 | @Override 70 | public boolean dependsOnFileContent() { 71 | return true; 72 | } 73 | 74 | @Override 75 | public int getVersion() { 76 | return 1; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/model/ModelReference.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.model; 2 | 3 | import com.intellij.util.indexing.ID; 4 | import pers.fw.tplugin.beans.LaravelIcons; 5 | import com.intellij.codeInsight.lookup.LookupElement; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.lang.Language; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.patterns.PlatformPatterns; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.PsiManager; 13 | import com.intellij.psi.search.GlobalSearchScope; 14 | import com.intellij.util.Processor; 15 | import com.intellij.util.indexing.FileBasedIndex; 16 | import com.jetbrains.php.lang.PhpFileType; 17 | import com.jetbrains.php.lang.PhpLanguage; 18 | import com.jetbrains.php.lang.psi.elements.*; 19 | import pers.fw.tplugin.config.CollectProjectUniqueKeys; 20 | import pers.fw.tplugin.inter.GotoCompletionContributor; 21 | import pers.fw.tplugin.inter.GotoCompletionLanguageRegistrar; 22 | import pers.fw.tplugin.inter.GotoCompletionProvider; 23 | import pers.fw.tplugin.inter.GotoCompletionRegistrarParameter; 24 | import org.apache.commons.lang.StringUtils; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | import pers.fw.tplugin.util.PsiElementUtil; 28 | import pers.fw.tplugin.util.Tool; 29 | import pers.fw.tplugin.util.Util; 30 | 31 | import java.util.*; 32 | 33 | public class ModelReference implements GotoCompletionLanguageRegistrar { 34 | // private static MethodMatcher.CallToSignature[] CONFIG = new MethodMatcher.CallToSignature[]{ 35 | // new MethodMatcher.CallToSignature("\\think\\Loader", "pers.fw.tplugin.model") 36 | // }; 37 | 38 | @Override 39 | public boolean support(@NotNull Language language) { 40 | return PhpLanguage.INSTANCE == language; 41 | } 42 | 43 | @Override 44 | public void register(GotoCompletionRegistrarParameter registrar) { 45 | registrar.register(PlatformPatterns.psiElement(), new GotoCompletionContributor() { 46 | @Nullable 47 | @Override 48 | public GotoCompletionProvider getProvider(@Nullable PsiElement psiElement) { 49 | if (psiElement == null) {// || !LaravelProjectComponent.isEnabled(psiElement)) { 50 | return null; 51 | } 52 | 53 | PsiElement parent = psiElement.getParent(); 54 | 55 | if(parent == null)return null; 56 | if (PsiElementUtil.isFunctionReference(parent, "model", 0)) { 57 | return new ModelReference.ModelProvider(parent); 58 | }else if(PsiElementUtil.isFunctionReference(parent, "controller", 0)){ 59 | return new ModelReference.ControllerProvider(parent); 60 | } 61 | return null; 62 | } 63 | }); 64 | } 65 | 66 | public static class ControllerProvider extends ModelReference.ModelProvider { 67 | 68 | public ControllerProvider(PsiElement element) { 69 | super(element); 70 | this.key=ControllerStubIndex.KEY; 71 | } 72 | } 73 | 74 | private static class ModelProvider extends GotoCompletionProvider { 75 | 76 | protected ID key=ModelStubIndex.KEY; 77 | 78 | public ModelProvider(PsiElement element) { 79 | super(element); 80 | } 81 | 82 | @NotNull 83 | @Override 84 | public Collection getLookupElements() { 85 | 86 | 87 | final Collection lookupElements = new ArrayList<>(); 88 | CollectProjectUniqueKeys ymlProjectProcessor = new CollectProjectUniqueKeys(getProject(), key); 89 | FileBasedIndex.getInstance().processAllKeys(key, ymlProjectProcessor, getProject()); 90 | String curModule = Util.getCurTpModuleName(getElement()) + "/"; 91 | for (String key : ymlProjectProcessor.getResult()) { //从ymlProjectProcessor中获取结果 92 | lookupElements.add(LookupElementBuilder.create(key).withIcon(LaravelIcons.TEMPLATE_CONTROLLER_LINE_MARKER)); 93 | if (key.startsWith(curModule)) { //去掉前缀的模型提示 94 | key = key.replace(curModule, ""); 95 | lookupElements.add(LookupElementBuilder.create(key).withIcon(LaravelIcons.TEMPLATE_CONTROLLER_LINE_MARKER)); 96 | } 97 | } 98 | return lookupElements; 99 | } 100 | 101 | @NotNull 102 | @Override 103 | public Collection getPsiTargets(StringLiteralExpression psiElement) { 104 | // return super.getPsiTargets(psiElement, offset, editor); 105 | final Set targets = new HashSet<>(); 106 | 107 | String contents = psiElement.getContents(); 108 | if (StringUtils.isBlank(contents)) { 109 | return targets; 110 | } 111 | 112 | //判断是否有模块名 113 | String curTpModuleName = Util.getCurTpModuleName(getElement())+"/"; 114 | String content2=""; 115 | if(!contents.startsWith(curTpModuleName)){ 116 | content2=curTpModuleName+contents; 117 | } 118 | 119 | //忽略大小写 120 | Collection allKeys = FileBasedIndex.getInstance().getAllKeys(key, getElement().getProject()); 121 | 122 | contents = Util.getKeyWithCase(allKeys, contents); 123 | if(!allKeys.contains(contents)) 124 | contents = Util.getKeyWithCase(allKeys, content2); 125 | 126 | FileBasedIndex.getInstance().getFilesWithKey(key, new HashSet<>(Collections.singletonList(contents)), 127 | new Processor() { 128 | @Override 129 | public boolean process(VirtualFile virtualFile) { 130 | if (virtualFile != null) { 131 | PsiFile psiFileTarget = PsiManager.getInstance(ModelReference.ModelProvider.this.getProject()).findFile(virtualFile); 132 | targets.add(psiFileTarget); 133 | } 134 | return true; 135 | } 136 | }, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(getProject()), PhpFileType.INSTANCE)); 137 | 138 | return targets; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/model/ModelStubIndex.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.model; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import com.intellij.util.indexing.*; 5 | import com.intellij.util.io.DataExternalizer; 6 | import com.intellij.util.io.EnumeratorStringDescriptor; 7 | import com.intellij.util.io.KeyDescriptor; 8 | import com.intellij.util.io.VoidDataExternalizer; 9 | import com.jetbrains.php.lang.PhpFileType; 10 | import com.jetbrains.php.lang.psi.PhpFile; 11 | import gnu.trove.THashMap; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | import java.util.Map; 15 | 16 | public class ModelStubIndex extends FileBasedIndexExtension { 17 | public static final ID KEY = ID.create("fw.idea.thinkphp.model"); 18 | private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); 19 | 20 | @NotNull 21 | @Override 22 | public ID getName() { 23 | return KEY; 24 | } 25 | 26 | @NotNull 27 | @Override 28 | public DataIndexer getIndexer() { 29 | return new DataIndexer() { 30 | @NotNull 31 | @Override 32 | public Map map(@NotNull FileContent fileContent) { 33 | final Map map = new THashMap<>(); 34 | 35 | PsiFile psiFile = fileContent.getPsiFile(); 36 | if (!(psiFile instanceof PhpFile)) { 37 | return map; 38 | } 39 | psiFile.getName(); 40 | //匹配文件 41 | String model = ModelUtil.matchModelFile(fileContent.getProject(), fileContent.getFile()); 42 | if (model != null) 43 | map.put(model, null); 44 | return map; 45 | } 46 | }; 47 | } 48 | 49 | @NotNull 50 | @Override 51 | public KeyDescriptor getKeyDescriptor() { 52 | return this.myKeyDescriptor; 53 | } 54 | 55 | @NotNull 56 | @Override 57 | public DataExternalizer getValueExternalizer() { 58 | return VoidDataExternalizer.INSTANCE; 59 | } 60 | 61 | @NotNull 62 | @Override 63 | public FileBasedIndex.InputFilter getInputFilter() { 64 | return file -> file.getFileType() == PhpFileType.INSTANCE; 65 | } 66 | 67 | @Override 68 | public boolean dependsOnFileContent() { 69 | return true; 70 | } 71 | 72 | @Override 73 | public int getVersion() { 74 | return 1; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/model/ModelUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.model; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | public class ModelUtil { 10 | private static final Pattern modelFilePattern = Pattern.compile(".*/application/(\\w+)/(model|logic|service)/(.+).php$"); 11 | private static final Pattern controllerFilePattern = Pattern.compile(".*/application/(\\w+)/controller/(\\w+).php$"); 12 | 13 | public static String matchModelFile(Project project, VirtualFile virtualFile) { 14 | String path = virtualFile.getPath(); 15 | String projectPath = project.getBaseDir().getPath(); 16 | 17 | if (path.startsWith(projectPath)) { 18 | path = path.substring(projectPath.length()); 19 | } 20 | Matcher matcher = modelFilePattern.matcher(path); 21 | if (matcher.matches()) { 22 | return matcher.group(1)+"/"+matcher.group(3); 23 | } else { 24 | return null; 25 | } 26 | } 27 | public static String matchControllerFile(Project project, VirtualFile virtualFile) { 28 | String path = virtualFile.getPath(); 29 | String projectPath = project.getBaseDir().getPath(); 30 | 31 | if (path.startsWith(projectPath)) { 32 | path = path.substring(projectPath.length()); 33 | } 34 | Matcher matcher = controllerFilePattern.matcher(path); 35 | if (matcher.matches()) { 36 | return matcher.group(1)+"/"+matcher.group(2); 37 | } else { 38 | return null; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/router/PhpControllerVisitor.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.router; 2 | 3 | import pers.fw.tplugin.beans.ArrayKeyVisitor; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiRecursiveElementWalkingVisitor; 6 | import com.jetbrains.php.lang.psi.elements.Method; 7 | import com.jetbrains.php.lang.psi.elements.PhpClass; 8 | 9 | public class PhpControllerVisitor extends PsiRecursiveElementWalkingVisitor { 10 | 11 | private final String prefix; 12 | private final ArrayKeyVisitor visitor; 13 | 14 | public PhpControllerVisitor(String prefix, ArrayKeyVisitor visitor) { 15 | this.prefix = prefix; 16 | this.visitor = visitor; 17 | } 18 | 19 | //递归遍历psi元素 20 | @Override 21 | public void visitElement(PsiElement element) { 22 | if (element instanceof PhpClass) { 23 | PsiElement[] childrens = element.getChildren(); 24 | for (PsiElement item : childrens) { 25 | if (item instanceof Method) { 26 | Method method = (Method) item; 27 | String route = this.prefix + "/" + method.getName(); 28 | this.visitor.visit(route, method, false); 29 | } 30 | } 31 | } else { 32 | super.visitElement(element); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/router/RouteUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.router; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.PsiFile; 7 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression; 8 | import com.jetbrains.php.lang.psi.elements.ArrayHashElement; 9 | import com.jetbrains.php.lang.psi.elements.impl.PhpReturnImpl; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.debugger.values.ArrayValue; 12 | import pers.fw.tplugin.util.Tool; 13 | 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | public class RouteUtil { 18 | 19 | private static final Pattern controllerFilePattern = Pattern.compile(".*/application/(\\w+)/controller/(\\w+).php$"); 20 | 21 | public static Boolean isRouteFile(PsiFile file) { 22 | //规约: 在application 路径下,文件名为route或xxx route xxx的文件 23 | String name = file.getName(); 24 | // Project project = file.getProject(); 25 | // String replace = name.replace(project.getBasePath() + "application/", "").replace(".php", ""); 26 | // String route = replace.toLowerCase(); 27 | // if (route.contains("/") || !route.contains("route")) { 28 | if (name.toLowerCase().contains("route")) { 29 | return true; 30 | } 31 | return false; 32 | } 33 | 34 | //判断当前位置是否是route的目录 35 | public static Boolean isRoutePosition(PsiElement element) { 36 | 37 | PsiElement parent1 = element.getParent().getParent(); 38 | if (parent1 instanceof ArrayCreationExpression) { 39 | PsiElement parent2 = parent1.getParent().getParent().getParent().getParent(); 40 | if (parent2 instanceof PhpReturnImpl) { 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | 47 | public static Boolean isArrayValue(PsiElement element) { 48 | // if (element instanceof ) { 49 | // return true; 50 | // } 51 | return false; 52 | } 53 | 54 | public static ControllerFileMatchResult matchControllerFile(Project project, VirtualFile virtualFile) { 55 | String path = virtualFile.getPath(); 56 | String projectPath = project.getBasePath(); 57 | if (projectPath != null) { 58 | path = path.substring(projectPath.length()); 59 | } 60 | Matcher matcher = controllerFilePattern.matcher(path); 61 | if (matcher.matches()) { 62 | String prefix1 = matcher.group(1); 63 | String prefix2 = matcher.group(2); 64 | String prefix = prefix1 + "/" + prefix2; 65 | return new ControllerFileMatchResult(true, prefix); 66 | } else { 67 | return new ControllerFileMatchResult(false, ""); 68 | } 69 | } 70 | 71 | public static class ControllerFileMatchResult { 72 | private boolean matches; 73 | 74 | private String keyPrefix; 75 | 76 | ControllerFileMatchResult(boolean matches, @NotNull String keyPrefix) { 77 | this.matches = matches; 78 | this.keyPrefix = keyPrefix; 79 | } 80 | 81 | public boolean matches() { 82 | return matches; 83 | } 84 | 85 | @NotNull 86 | public String getKeyPrefix() { 87 | return keyPrefix; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/router/RouteValStubIndex.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.router; 2 | 3 | import pers.fw.tplugin.beans.ArrayKeyVisitor; 4 | import com.intellij.psi.PsiElement; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.util.indexing.*; 7 | import com.intellij.util.io.DataExternalizer; 8 | import com.intellij.util.io.EnumeratorStringDescriptor; 9 | import com.intellij.util.io.KeyDescriptor; 10 | import com.intellij.util.io.VoidDataExternalizer; 11 | import com.jetbrains.php.lang.PhpFileType; 12 | import com.jetbrains.php.lang.psi.PhpFile; 13 | import gnu.trove.THashMap; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.Map; 17 | 18 | 19 | public class RouteValStubIndex extends FileBasedIndexExtension { 20 | 21 | public static final ID KEY = ID.create("fw.router.index"); 22 | private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); 23 | 24 | @NotNull 25 | @Override 26 | public ID getName() { 27 | return KEY; 28 | } 29 | 30 | //收集数据 索引进程, 数据文件被修改不会立即触发该方法, 只有当动作(例如代码补全)发生才会触发该方法 31 | @NotNull 32 | @Override 33 | public DataIndexer getIndexer() { 34 | return new DataIndexer() { 35 | @NotNull 36 | @Override 37 | public Map map(@NotNull FileContent fileContent) { //fileContent, 被修改的文件,controller文件 38 | final Map map = new THashMap<>(); //创建结果容器 39 | PsiFile psiFile = fileContent.getPsiFile(); //获取Psi 40 | if (!(psiFile instanceof PhpFile)) { //过滤非php文件 41 | return map; 42 | } 43 | //匹配文件 44 | RouteUtil.ControllerFileMatchResult controllerFileMatchResult = RouteUtil.matchControllerFile(fileContent.getProject(), fileContent.getFile()); 45 | if (controllerFileMatchResult.matches()) { //是controller文件 46 | String keyPrefix = controllerFileMatchResult.getKeyPrefix(); 47 | if (keyPrefix != null) 48 | map.put(keyPrefix, null); 49 | psiFile.acceptChildren(new PhpControllerVisitor(keyPrefix, new ArrayKeyVisitor() { 50 | @Override 51 | public void visit(String key, PsiElement psiKey, boolean isRootElement) { 52 | map.put(key, null); 53 | } 54 | })); 55 | } 56 | return map; 57 | } 58 | }; 59 | } 60 | 61 | @NotNull 62 | @Override 63 | public KeyDescriptor getKeyDescriptor() { 64 | return myKeyDescriptor; 65 | } 66 | 67 | @NotNull 68 | @Override 69 | public DataExternalizer getValueExternalizer() { 70 | return VoidDataExternalizer.INSTANCE; 71 | } 72 | 73 | @Override 74 | public int getVersion() { 75 | return 1; 76 | } 77 | 78 | @NotNull 79 | @Override 80 | public FileBasedIndex.InputFilter getInputFilter() { 81 | return file -> file.getFileType() == PhpFileType.INSTANCE; 82 | } 83 | 84 | @Override 85 | public boolean dependsOnFileContent() { 86 | return true; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/router/RouterReference.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.router; 2 | 3 | import pers.fw.tplugin.beans.ArrayKeyVisitor; 4 | import pers.fw.tplugin.beans.LaravelIcons; 5 | import com.intellij.codeInsight.lookup.LookupElement; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.lang.Language; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.patterns.PlatformPatterns; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.PsiManager; 13 | import com.intellij.psi.search.GlobalSearchScope; 14 | import com.intellij.util.Processor; 15 | import com.intellij.util.indexing.FileBasedIndex; 16 | import com.jetbrains.php.lang.PhpFileType; 17 | import com.jetbrains.php.lang.PhpLanguage; 18 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 19 | import pers.fw.tplugin.config.CollectProjectUniqueKeys; 20 | import pers.fw.tplugin.inter.GotoCompletionContributor; 21 | import pers.fw.tplugin.inter.GotoCompletionLanguageRegistrar; 22 | import pers.fw.tplugin.inter.GotoCompletionProvider; 23 | import pers.fw.tplugin.inter.GotoCompletionRegistrarParameter; 24 | import org.apache.commons.lang.StringUtils; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | import pers.fw.tplugin.model.ModelStubIndex; 28 | import pers.fw.tplugin.util.MethodMatcher; 29 | import pers.fw.tplugin.util.PsiElementUtil; 30 | import pers.fw.tplugin.util.Util; 31 | 32 | import java.util.*; 33 | 34 | public class RouterReference implements GotoCompletionLanguageRegistrar { 35 | 36 | private static MethodMatcher.CallToSignature[] Router = new MethodMatcher.CallToSignature[]{ 37 | new MethodMatcher.CallToSignature("\\think\\Route", "get"), 38 | new MethodMatcher.CallToSignature("\\think\\Route", "any"), 39 | new MethodMatcher.CallToSignature("\\think\\Route", "post"), 40 | new MethodMatcher.CallToSignature("\\think\\Route", "put"), 41 | new MethodMatcher.CallToSignature("\\think\\Route", "delete"), 42 | new MethodMatcher.CallToSignature("\\think\\Route", "resource"), 43 | new MethodMatcher.CallToSignature("\\think\\Route", "rule"), 44 | new MethodMatcher.CallToSignature("\\think\\Route", "controller"), 45 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "get"), 46 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "any"), 47 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "post"), 48 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "put"), 49 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "delete"), 50 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "resource"), 51 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "rule"), 52 | new MethodMatcher.CallToSignature("\\think\\facade\\Route", "controller"), 53 | }; 54 | 55 | 56 | @Override 57 | public void register(GotoCompletionRegistrarParameter registrar) { 58 | registrar.register(PlatformPatterns.psiElement(), new GotoCompletionContributor() { 59 | @Nullable 60 | @Override 61 | public GotoCompletionProvider getProvider(@Nullable PsiElement psiElement) { 62 | if (psiElement == null) {// || !LaravelProjectComponent.isEnabled(psiElement)) { 63 | return null; 64 | } 65 | //string 66 | PsiElement parent = psiElement.getParent(); 67 | PsiFile containingFile = psiElement.getContainingFile(); 68 | containingFile.getName(); 69 | if (parent != null && 70 | (Util.isHintMethod(parent, Router, 1, true) 71 | || PsiElementUtil.isFunctionReference(parent, "route", 1) 72 | || (RouteUtil.isRouteFile(containingFile) && RouteUtil.isRoutePosition(parent)))) { 73 | return new RouteProvider(parent); 74 | } 75 | 76 | return null; 77 | } 78 | }); 79 | } 80 | 81 | @Override 82 | public boolean support(@NotNull Language language) { 83 | return PhpLanguage.INSTANCE == language; 84 | } 85 | 86 | private static class RouteProvider extends GotoCompletionProvider { 87 | 88 | public RouteProvider(PsiElement element) { 89 | super(element); 90 | } 91 | 92 | /** 93 | * 获取提示信息 94 | * 95 | * @return 返回提示集合 96 | */ 97 | @NotNull 98 | @Override 99 | public Collection getLookupElements() { 100 | 101 | final Collection lookupElements = new ArrayList<>(); 102 | 103 | CollectProjectUniqueKeys ymlProjectProcessor = new CollectProjectUniqueKeys(getProject(), RouteValStubIndex.KEY); 104 | //扫描文件获取key, 放入ymlProjectProcessor 105 | FileBasedIndex.getInstance().processAllKeys(RouteValStubIndex.KEY, ymlProjectProcessor, getProject()); 106 | for (String key : ymlProjectProcessor.getResult()) { //从ymlProjectProcessor中获取结果 107 | lookupElements.add(LookupElementBuilder.create(key).withIcon(LaravelIcons.ROUTE)); 108 | } 109 | 110 | return lookupElements; 111 | } 112 | 113 | @NotNull 114 | @Override 115 | public Collection getPsiTargets(StringLiteralExpression element) { 116 | 117 | final Set targets = new HashSet<>(); 118 | 119 | String contents = element.getContents(); 120 | if (StringUtils.isBlank(contents)) { 121 | return targets; 122 | } 123 | 124 | //忽略大小写 125 | Collection allKeys = FileBasedIndex.getInstance().getAllKeys(RouteValStubIndex.KEY, getElement().getProject()); 126 | 127 | final String contents1 = Util.getKeyWithCase(allKeys, contents); 128 | 129 | FileBasedIndex.getInstance().getFilesWithKey(RouteValStubIndex.KEY, new HashSet<>(Collections.singletonList(contents1)), new Processor() { 130 | @Override 131 | public boolean process(VirtualFile virtualFile) { 132 | PsiFile psiFileTarget = PsiManager.getInstance(RouteProvider.this.getProject()).findFile(virtualFile); 133 | if (psiFileTarget == null) { 134 | return true; 135 | } 136 | String[] split = contents.split("/"); 137 | if (split.length == 2) 138 | targets.add(psiFileTarget); 139 | psiFileTarget.acceptChildren(new PhpControllerVisitor(RouteUtil.matchControllerFile(RouteProvider.this.getProject(), virtualFile).getKeyPrefix(), 140 | new ArrayKeyVisitor() { 141 | @Override 142 | public void visit(String key, PsiElement psiKey, boolean isRootElement) { 143 | if (!isRootElement && key.equals(contents1)) { 144 | targets.add(psiKey); 145 | } 146 | } 147 | })); 148 | 149 | return true; 150 | } 151 | }, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(getProject()), PhpFileType.INSTANCE)); 152 | 153 | return targets; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/GotoCompletionUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import com.intellij.patterns.ElementPattern; 4 | import com.intellij.psi.PsiElement; 5 | import pers.fw.tplugin.config.AppConfigReferences; 6 | import pers.fw.tplugin.db.DbReference; 7 | import pers.fw.tplugin.inter.GotoCompletionContributor; 8 | import pers.fw.tplugin.inter.GotoCompletionLanguageRegistrar; 9 | import pers.fw.tplugin.inter.GotoCompletionRegistrar; 10 | import pers.fw.tplugin.inter.GotoCompletionRegistrarParameter; 11 | import pers.fw.tplugin.model.ModelReference; 12 | import org.jetbrains.annotations.NotNull; 13 | import pers.fw.tplugin.router.RouterReference; 14 | import pers.fw.tplugin.view.ViewReferences2; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collection; 18 | 19 | public class GotoCompletionUtil { 20 | 21 | private static GotoCompletionRegistrar[] CONTRIBUTORS = new GotoCompletionRegistrar[]{ 22 | new AppConfigReferences(), 23 | new ViewReferences2(), 24 | new RouterReference(), 25 | new DbReference(), 26 | new ModelReference(), 27 | }; 28 | 29 | // private static GotoCompletionRegistrar[] = new GotoCompletionRegistrar[]{ 30 | // new AppConfigReferences(), 31 | // new ViewReferences2(), 32 | // new RouterReference(), 33 | // new ModelReference(), 34 | // }; 35 | 36 | public static Collection getContributors(final PsiElement psiElement) { 37 | Collection contributors = new ArrayList<>(); 38 | 39 | GotoCompletionRegistrarParameter registerParam = new GotoCompletionRegistrarParameter() { 40 | @Override 41 | public void register(@NotNull ElementPattern pattern, GotoCompletionContributor contributor) { 42 | if (pattern.accepts(psiElement)) { 43 | contributors.add(contributor); 44 | } 45 | } 46 | }; 47 | 48 | for (GotoCompletionRegistrar register : CONTRIBUTORS) { 49 | // filter on language, 根据当前文件语言过滤处理对象 50 | if (register instanceof GotoCompletionLanguageRegistrar) { 51 | if (((GotoCompletionLanguageRegistrar) register).support(psiElement.getLanguage())) { 52 | register.register(registerParam); 53 | // ((ArrayList) contributors).add(((AppConfigReferences)register).); 54 | } 55 | } else { 56 | register.register(registerParam); 57 | } 58 | } 59 | 60 | return contributors; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/LaravelProjectComponent.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import com.intellij.openapi.components.ProjectComponent; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.vfs.VfsUtil; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import com.intellij.psi.PsiElement; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | /** 12 | * @author Daniel Espendiller 13 | */ 14 | public class LaravelProjectComponent implements ProjectComponent { 15 | private Project project; 16 | 17 | public LaravelProjectComponent(Project project) { 18 | this.project = project; 19 | } 20 | 21 | @Override 22 | public void projectOpened() { 23 | notifyPluginEnableDialog(); 24 | } 25 | 26 | @Override 27 | public void projectClosed() { 28 | 29 | } 30 | 31 | @Override 32 | public void initComponent() { 33 | 34 | } 35 | 36 | @Override 37 | public void disposeComponent() { 38 | 39 | } 40 | 41 | @NotNull 42 | @Override 43 | public String getComponentName() { 44 | return "LaravelProjectComponent"; 45 | } 46 | 47 | public static boolean isEnabled(Project project) { 48 | return LaravelSettings.getInstance(project).pluginEnabled; 49 | } 50 | 51 | public static boolean isEnabled(@Nullable PsiElement psiElement) { 52 | return psiElement != null && isEnabled(psiElement.getProject()); 53 | } 54 | public static boolean isEnabledForIndex(@Nullable Project project) { 55 | 56 | if(project == null) { 57 | return false; 58 | } 59 | 60 | if(isEnabled(project)) { 61 | return true; 62 | } 63 | 64 | VirtualFile baseDir = project.getBaseDir(); 65 | return VfsUtil.findRelativeFile(baseDir, "vendor", "laravel") != null; 66 | } 67 | 68 | private void notifyPluginEnableDialog() { 69 | // Enable Project dialog 70 | if(!isEnabled(this.project) && !LaravelSettings.getInstance(this.project).dismissEnableNotification) { 71 | if(VfsUtil.findRelativeFile(this.project.getBaseDir(), "app") != null 72 | && VfsUtil.findRelativeFile(this.project.getBaseDir(), "vendor", "laravel") != null 73 | ) { 74 | // IdeHelper.notifyEnableMessage(project); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/LaravelSettings.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 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.openapi.project.Project; 8 | import com.intellij.util.xmlb.XmlSerializerUtil; 9 | import org.apache.commons.lang.StringUtils; 10 | import org.jetbrains.annotations.Nullable; 11 | import pers.fw.tplugin.view.dict.TemplatePath; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * @author Daniel Espendiller 18 | */ 19 | @State( 20 | name = "LaravelPluginSettings", 21 | storages = { 22 | @Storage("laravel-plugin.xml") 23 | } 24 | ) 25 | public class LaravelSettings implements PersistentStateComponent { 26 | 27 | public boolean pluginEnabled = false; 28 | public boolean useAutoPopup = false; 29 | public String routerNamespace; 30 | public String mainLanguage; 31 | 32 | @Nullable 33 | public List templatePaths = new ArrayList<>(); 34 | 35 | public boolean dismissEnableNotification = false; 36 | 37 | public static LaravelSettings getInstance(Project project) { 38 | return ServiceManager.getService(project, LaravelSettings.class); 39 | } 40 | 41 | public String getMainLanguage() { 42 | return !StringUtils.isBlank(mainLanguage) ? mainLanguage : "en"; 43 | } 44 | 45 | @Nullable 46 | @Override 47 | public LaravelSettings getState() { 48 | return this; 49 | } 50 | 51 | @Override 52 | public void loadState(LaravelSettings settings) { 53 | XmlSerializerUtil.copyBean(settings, this); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/MethodMatcher.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import pers.fw.tplugin.beans.MethodReferenceBag; 4 | import pers.fw.tplugin.beans.ParameterBag; 5 | import com.intellij.psi.PsiElement; 6 | import com.jetbrains.php.lang.psi.elements.Method; 7 | import com.jetbrains.php.lang.psi.elements.MethodReference; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.Collection; 13 | import java.util.List; 14 | 15 | public class MethodMatcher { 16 | 17 | @Nullable 18 | public static MethodMatcher.MethodMatchParameter getMatchedSignatureWithDepth(PsiElement psiElement, MethodMatcher.CallToSignature[] callToSignatures) { 19 | return getMatchedSignatureWithDepth(psiElement, callToSignatures, 0); 20 | } 21 | 22 | @Nullable 23 | public static MethodMatcher.MethodMatchParameter getMatchedSignatureWithDepth(PsiElement psiElement, MethodMatcher.CallToSignature[] callToSignatures, int defaultParameterIndex) { 24 | 25 | // if (!LaravelProjectComponent.isEnabled(psiElement)) { 26 | // return null; 27 | // } 28 | 29 | MethodMatcher.MethodMatchParameter methodMatchParameter = new MethodMatcher.StringParameterMatcher(psiElement, defaultParameterIndex) 30 | .withSignature(callToSignatures) 31 | .match(); 32 | 33 | if(methodMatchParameter != null) { 34 | return methodMatchParameter; 35 | } 36 | 37 | // try on resolved method 38 | return new MethodMatcher.StringParameterRecursiveMatcher(psiElement, defaultParameterIndex) 39 | .withSignature(callToSignatures) 40 | .match(); 41 | } 42 | 43 | public static class CallToSignature { 44 | 45 | private final String instance; 46 | private final String method; 47 | 48 | public CallToSignature(String instance, String method) { 49 | this.instance = instance; 50 | this.method = method; 51 | } 52 | 53 | public String getInstance() { 54 | return instance; 55 | } 56 | 57 | public String getMethod() { 58 | return method; 59 | } 60 | } 61 | 62 | public static class MethodMatchParameter { 63 | 64 | final private CallToSignature signature; 65 | final private ParameterBag parameterBag; 66 | final private PsiElement[] parameters; 67 | final private MethodReference methodReference; 68 | 69 | public MethodMatchParameter(CallToSignature signature, ParameterBag parameterBag, PsiElement[] parameters, MethodReference methodReference) { 70 | this.signature = signature; 71 | this.parameterBag = parameterBag; 72 | this.parameters = parameters; 73 | this.methodReference = methodReference; 74 | } 75 | 76 | @Nullable 77 | public CallToSignature getSignature() { 78 | return signature; 79 | } 80 | 81 | public ParameterBag getParameterBag() { 82 | return this.parameterBag; 83 | } 84 | 85 | public PsiElement[] getParameters() { 86 | return parameters; 87 | } 88 | 89 | public MethodReference getMethodReference() { 90 | return methodReference; 91 | } 92 | 93 | } 94 | 95 | public static class StringParameterMatcher extends AbstractMethodParameterMatcher { 96 | 97 | public StringParameterMatcher(PsiElement psiElement, int parameterIndex) { 98 | super(psiElement, parameterIndex); 99 | } 100 | 101 | @Nullable 102 | public MethodMatchParameter match() { 103 | 104 | // if (!LaravelProjectComponent.isEnabled(psiElement)) { 105 | // return null; 106 | // } 107 | //获取方法引用对象 108 | MethodReferenceBag bag = PhpElementsUtil.getMethodParameterReferenceBag(psiElement, this.parameterIndex); 109 | if(bag == null) { 110 | return null; 111 | } 112 | 113 | CallToSignature matchedMethodSignature = this.isCallTo(bag.getMethodReference()); 114 | if(matchedMethodSignature == null) { 115 | return null; 116 | } 117 | 118 | return new MethodMatchParameter(matchedMethodSignature, 119 | bag.getParameterBag(), 120 | bag.getParameterList().getParameters(), 121 | bag.getMethodReference()); 122 | } 123 | 124 | } 125 | 126 | public static class StringParameterRecursiveMatcher extends AbstractMethodParameterMatcher { 127 | 128 | public StringParameterRecursiveMatcher(PsiElement psiElement, int parameterIndex) { 129 | super(psiElement, parameterIndex); 130 | } 131 | 132 | @Nullable 133 | public MethodMatchParameter match() { 134 | 135 | // if (!LaravelProjectComponent.isEnabled(psiElement)) { 136 | // return null; 137 | // } 138 | 139 | MethodReferenceBag bag = PhpElementsUtil.getMethodParameterReferenceBag(psiElement); 140 | if(bag == null) { 141 | return null; 142 | } 143 | 144 | // try on current method 145 | MethodMatcher.MethodMatchParameter methodMatchParameter = new StringParameterMatcher(psiElement, parameterIndex) 146 | .withSignature(this.signatures) 147 | .match(); 148 | 149 | if(methodMatchParameter != null) { 150 | return methodMatchParameter; 151 | } 152 | 153 | // walk down next method 154 | MethodReference methodReference = bag.getMethodReference(); 155 | Method method = Symfony2InterfacesUtil.getMultiResolvedMethod(methodReference); 156 | if(method == null) { 157 | return null; 158 | } 159 | // 所有参数列表, 160 | PsiElement[] parameterReferences = PhpElementsUtil.getMethodParameterReferences(method, bag.getParameterBag().getIndex()); 161 | if(parameterReferences == null || parameterReferences.length == 0) { 162 | return null; 163 | } 164 | 165 | for(PsiElement var: parameterReferences) { 166 | 167 | MethodMatcher.MethodMatchParameter methodMatchParameterRef = new MethodMatcher.StringParameterMatcher(var, parameterIndex) 168 | .withSignature(this.signatures) 169 | .match(); 170 | 171 | if(methodMatchParameterRef != null) { 172 | return methodMatchParameterRef; 173 | } 174 | 175 | } 176 | 177 | return null; 178 | 179 | } 180 | 181 | } 182 | 183 | public interface MethodParameterMatcherInterface { 184 | @Nullable 185 | public MethodMatchParameter match(); 186 | } 187 | 188 | public abstract static class AbstractMethodParameterMatcher implements MethodParameterMatcherInterface { 189 | 190 | final protected List signatures; 191 | final protected int parameterIndex; 192 | final protected PsiElement psiElement; 193 | 194 | public AbstractMethodParameterMatcher(PsiElement psiElement, int parameterIndex) { 195 | this.signatures = new ArrayList(); 196 | this.parameterIndex = parameterIndex; 197 | this.psiElement = psiElement; 198 | } 199 | 200 | public AbstractMethodParameterMatcher withSignature(String instance, String method) { 201 | this.signatures.add(new CallToSignature(instance, method)); 202 | return this; 203 | } 204 | 205 | public AbstractMethodParameterMatcher withSignature(Collection signatures) { 206 | this.signatures.addAll(signatures); 207 | return this; 208 | } 209 | 210 | public AbstractMethodParameterMatcher withSignature(CallToSignature[] callToSignatures) { 211 | this.signatures.addAll(Arrays.asList(callToSignatures)); 212 | return this; 213 | } 214 | 215 | // 是否是该方法 216 | @Nullable 217 | protected CallToSignature isCallTo(MethodReference methodReference) { 218 | Symfony2InterfacesUtil interfacesUtil = new Symfony2InterfacesUtil(); 219 | 220 | for(CallToSignature signature: this.signatures) { 221 | if(interfacesUtil.isCallTo(methodReference, signature.getInstance(), signature.getMethod())) { 222 | return signature; 223 | } 224 | } 225 | 226 | return null; 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/PsiElementUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import pers.fw.tplugin.beans.ParameterBag; 4 | import com.intellij.psi.PsiElement; 5 | import com.jetbrains.php.lang.psi.elements.FunctionReference; 6 | import com.jetbrains.php.lang.psi.elements.Method; 7 | import com.jetbrains.php.lang.psi.elements.ParameterList; 8 | import com.jetbrains.php.lang.psi.elements.ParameterListOwner; 9 | import com.jetbrains.php.lang.psi.elements.impl.PhpClassImpl; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | public class PsiElementUtil { 14 | 15 | public static boolean isFunctionReference(@NotNull PsiElement psiElement, @NotNull String functionName, int parameterIndex) { 16 | 17 | 18 | PsiElement parameterList = psiElement.getParent(); 19 | PsiElement functionCall = parameterList.getParent(); 20 | if (parameterIndex != -1) { 21 | if (!(parameterList instanceof ParameterList)) { 22 | return false; 23 | } 24 | ParameterBag index = getCurrentParameterIndex(psiElement); 25 | if (index == null || index.getIndex() != parameterIndex) { 26 | return false; 27 | } 28 | } else { 29 | functionCall = psiElement; 30 | } 31 | 32 | if (!(functionCall instanceof FunctionReference)) { 33 | return false; 34 | } 35 | return functionName.equals(((FunctionReference) functionCall).getName()); 36 | } 37 | 38 | // public static boolean isFunctionReference(@NotNull PsiElement psiElement, @NotNull String functionName, int parameterIndex) { 39 | // 40 | // PsiElement parameterList = psiElement.getParent(); 41 | // if (!(parameterList instanceof ParameterList)) { 42 | // return false; 43 | // } 44 | // ParameterBag index = getCurrentParameterIndex(psiElement); 45 | // if (index == null || index.getIndex() != parameterIndex) { 46 | // return false; 47 | // } 48 | // PsiElement functionCall = parameterList.getParent(); 49 | // if (!(functionCall instanceof FunctionReference)) { 50 | // return false; 51 | // } 52 | // return functionName.equals(((FunctionReference) functionCall).getName()); 53 | // } 54 | 55 | @Nullable 56 | public static ParameterBag getCurrentParameterIndex(PsiElement psiElement) { 57 | 58 | if (!(psiElement.getContext() instanceof ParameterList)) { 59 | return null; 60 | } 61 | 62 | ParameterList parameterList = (ParameterList) psiElement.getContext(); 63 | if (!(parameterList.getContext() instanceof ParameterListOwner)) { 64 | return null; 65 | } 66 | 67 | return getCurrentParameterIndex(parameterList.getParameters(), psiElement); 68 | } 69 | 70 | @Nullable 71 | public static ParameterBag getCurrentParameterIndex(PsiElement[] parameters, PsiElement parameter) { 72 | int i; 73 | for (i = 0; i < parameters.length; i = i + 1) { 74 | if (parameters[i].equals(parameter)) { 75 | return new ParameterBag(i, parameters[i]); 76 | } 77 | } 78 | 79 | return null; 80 | } 81 | 82 | //获取当前方法名, 83 | public static Method getMethod(PsiElement psiElement) { 84 | if (psiElement == null) return null; 85 | PsiElement parent = psiElement.getParent(); 86 | if (parent instanceof Method) { 87 | return (Method) parent; 88 | } else { 89 | return getMethod(parent); 90 | } 91 | } 92 | 93 | public static PhpClassImpl getPhpClass(PsiElement psiElement) { 94 | if (psiElement == null) return null; 95 | PsiElement parent = psiElement.getParent(); 96 | if (parent instanceof PhpClassImpl) { 97 | return (PhpClassImpl) parent; 98 | } else { 99 | return getPhpClass(parent); 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/PsiElementUtils.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import pers.fw.tplugin.beans.ParameterBag; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.psi.PsiManager; 9 | import com.jetbrains.php.lang.psi.elements.FunctionReference; 10 | import com.jetbrains.php.lang.psi.elements.ParameterList; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collection; 16 | import java.util.HashSet; 17 | import java.util.List; 18 | 19 | /** 20 | * @author Daniel Espendiller 21 | */ 22 | public class PsiElementUtils { 23 | /** 24 | * getChildren fixed helper 25 | */ 26 | static public PsiElement[] getChildrenFix(PsiElement psiElement) { 27 | List psiElements = new ArrayList<>(); 28 | 29 | PsiElement startElement = psiElement.getFirstChild(); 30 | if(startElement == null) { 31 | return new PsiElement[0]; 32 | } 33 | 34 | psiElements.add(startElement); 35 | 36 | for (PsiElement child = psiElement.getFirstChild().getNextSibling(); child != null; child = child.getNextSibling()) { 37 | psiElements.add(child); 38 | } 39 | 40 | return psiElements.toArray(new PsiElement[psiElements.size()]); 41 | } 42 | 43 | @Nullable 44 | public static String trimQuote(@Nullable String text) { 45 | 46 | if(text == null) return null; 47 | 48 | return text.replaceAll("^\"|\"$|\'|\'$", ""); 49 | } 50 | 51 | public static boolean isFunctionReference(@NotNull PsiElement psiElement, @NotNull String functionName, int parameterIndex) { 52 | 53 | PsiElement parameterList = psiElement.getParent(); 54 | if(!(parameterList instanceof ParameterList)) { 55 | return false; 56 | } 57 | 58 | ParameterBag index = PhpElementsUtil.getCurrentParameterIndex(psiElement); 59 | if(index == null || index.getIndex() != parameterIndex) { 60 | return false; 61 | } 62 | 63 | PsiElement functionCall = parameterList.getParent(); 64 | if(!(functionCall instanceof FunctionReference)) { 65 | return false; 66 | } 67 | 68 | return functionName.equals(((FunctionReference) functionCall).getName()); 69 | } 70 | 71 | @NotNull 72 | public static Collection convertVirtualFilesToPsiFiles(@NotNull Project project, @NotNull Collection files) { 73 | Collection psiFiles = new HashSet<>(); 74 | 75 | PsiManager psiManager = null; 76 | for (VirtualFile file : files) { 77 | if(psiManager == null) { 78 | psiManager = PsiManager.getInstance(project); 79 | } 80 | 81 | PsiFile psiFile = psiManager.findFile(file); 82 | if(psiFile != null) { 83 | psiFiles.add(psiFile); 84 | } 85 | } 86 | 87 | return psiFiles; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/Tool.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.psi.search.GlobalSearchScope; 7 | import com.intellij.util.Processor; 8 | import com.intellij.util.indexing.FileBasedIndex; 9 | import com.jetbrains.php.lang.PhpFileType; 10 | import pers.fw.tplugin.router.RouteValStubIndex; 11 | 12 | import java.util.Collections; 13 | import java.util.HashSet; 14 | import java.util.List; 15 | 16 | public class Tool { 17 | public static void log(Object obj) { 18 | System.out.println(obj); 19 | } 20 | 21 | //打印当前psiElement,及它的子类 22 | public static void printPsiTree(PsiElement element) { 23 | if (element == null) { 24 | return; 25 | } 26 | level++; 27 | int i = 0; 28 | PsiElement[] elements = element.getChildren(); 29 | for (PsiElement item : elements) { 30 | i++; 31 | printeIndent(" ", level); 32 | String s = item.getClass().getSimpleName(); 33 | //level+"."+i+":"+ 34 | System.out.println(level + " " + s); 35 | printPsiTree(item); 36 | } 37 | level--; 38 | } 39 | 40 | private static int level = 0; 41 | 42 | private static void printeIndent(String str, int cnt) { 43 | for (int i = 0; i < cnt; i++) { 44 | System.out.print(str); 45 | } 46 | } 47 | 48 | public static void printFileIndex(Project project) { 49 | final String test = "index/Index2/test"; 50 | List strings = Collections.singletonList(test); 51 | System.out.println(strings); 52 | 53 | FileBasedIndex.getInstance().getFilesWithKey(RouteValStubIndex.KEY, new HashSet<>(strings), new Processor() { 54 | @Override 55 | public boolean process(VirtualFile virtualFile) { 56 | String name = virtualFile.getName(); 57 | System.out.println(name); 58 | return true; 59 | } 60 | }, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), PhpFileType.INSTANCE)); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/Util.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationFeature; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.PsiElement; 8 | import com.jetbrains.php.PhpIndex; 9 | import com.jetbrains.php.lang.psi.elements.Field; 10 | import com.jetbrains.php.lang.psi.elements.Method; 11 | import com.jetbrains.php.lang.psi.elements.MethodReference; 12 | import com.jetbrains.php.lang.psi.elements.PhpClass; 13 | import com.jetbrains.php.lang.psi.elements.impl.PhpClassImpl; 14 | import pers.fw.tplugin.beans.Config; 15 | import pers.fw.tplugin.beans.Setting; 16 | import pers.fw.tplugin.db.DbTableUtil; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.File; 20 | import java.io.FileReader; 21 | import java.util.*; 22 | 23 | public class 24 | Util { 25 | 26 | //获取当前引用变量的类 27 | public static PhpClass getInstanseClass(Project project, MethodReference methodRef) { 28 | Set types = methodRef.getDeclaredType().getTypes(); 29 | if (types.size() == 0) return null; 30 | String classType = null; 31 | for (String type : types) { 32 | if (type.contains("\\model\\")) { 33 | classType = type; 34 | break; 35 | } 36 | } 37 | if (classType == null) return null; 38 | String classFQN = classType.substring(classType.indexOf("\\"), classType.indexOf(".")); 39 | Collection classesByFQN = PhpIndex.getInstance(project).getClassesByFQN(classFQN); 40 | if (classesByFQN.size() == 0) return null; 41 | else 42 | return classesByFQN.iterator().next(); 43 | } 44 | 45 | //获取当前引用的方法 46 | public static Method getRefMethod(MethodReference methodRef) { 47 | PsiElement resolve = methodRef.resolve(); 48 | if (resolve instanceof Method) { 49 | return (Method) resolve; 50 | } 51 | return null; 52 | } 53 | 54 | //获取当前编辑的方法 55 | public static Method getMethod(PsiElement psiElement) { 56 | if (psiElement == null) return null; 57 | PsiElement parent = psiElement.getParent(); 58 | if (parent instanceof Method) { 59 | return (Method) parent; 60 | } else { 61 | return getMethod(parent); 62 | } 63 | } 64 | 65 | //获取当前编辑文件的类 66 | public static PhpClassImpl getPhpClass(PsiElement psiElement) { 67 | if (psiElement == null) return null; 68 | PsiElement parent = psiElement.getParent(); 69 | if (parent instanceof PhpClassImpl) { 70 | return (PhpClassImpl) parent; 71 | } else { 72 | return getPhpClass(parent); 73 | } 74 | } 75 | 76 | //处理前缀 77 | public static String rePrefix(String prefix) { 78 | String newPrefix = ""; 79 | int i = prefix.lastIndexOf("="); 80 | int j = prefix.lastIndexOf(","); 81 | int k = prefix.lastIndexOf("|"); 82 | int d = prefix.lastIndexOf("."); 83 | 84 | int max = Math.max(Math.max(k, Math.max(i, j)), d); 85 | if (max != -1) { 86 | newPrefix = prefix.substring(max + 1, prefix.length()); 87 | return newPrefix; 88 | } else { 89 | return prefix; 90 | } 91 | } 92 | 93 | 94 | /** 95 | * @param psiElement 96 | * @return 当前的模型目录; 根据类名获取 97 | */ 98 | public static String getCurTpModuleName(PsiElement psiElement) { 99 | PhpClassImpl phpClass = getPhpClass(psiElement); 100 | if(phpClass==null)return "xxx"; 101 | String fqn=phpClass.getFQN(); 102 | if(fqn==null)return "xxx"; 103 | String[] split = fqn.split("\\\\"); 104 | if (split.length > 2) { 105 | return split[2]; 106 | } 107 | return "xxx"; 108 | } 109 | 110 | public static String getTableByClass(PhpClass phpClass, Project project) { 111 | if (phpClass != null) { 112 | boolean isModel = false; 113 | Collection fields = phpClass.getFields(); 114 | for (Field item : fields) { 115 | if ("name".equals(item.getName())) { 116 | PsiElement defaultValue = item.getDefaultValue(); 117 | if(defaultValue==null)continue; 118 | String name = defaultValue.getText(); 119 | if (name != null && !name.isEmpty() && !"$name".equals(name)) { 120 | name = name.replace("'", "").replace("\"", ""); 121 | return DbTableUtil.getTableByName(project, name); 122 | } 123 | } else if ("table".equals(item.getName())) { 124 | PsiElement defaultValue = item.getDefaultValue(); 125 | if(defaultValue==null)continue; 126 | String table = defaultValue.getText(); 127 | if (table != null && !table.isEmpty() && !"$table".equals(table)) { 128 | table = table.replace("'", "").replace("\"", ""); 129 | return table; 130 | } 131 | } else if ("queryInstance".equals(item.getName())) { // 通过查询对象确定是否 model 132 | isModel = true; 133 | } 134 | } 135 | // if (isModel){ 136 | if (true){ //如果数据表名和当前类类名一样直接提示, 不管是不是模型,多提示也没事 137 | String tmp = phpClass.getName().replaceAll("[A-Z]", "_$0").toLowerCase(); 138 | if (tmp.indexOf("_") == 0){ 139 | tmp = tmp.substring(1); 140 | } 141 | return DbTableUtil.getTableByName(project, tmp); 142 | } 143 | } 144 | return null; 145 | } 146 | 147 | /** 148 | * @return application的目录, 相对目录, 相对于项目的目录 149 | */ 150 | public static String getApplicationDir(PsiElement psiElement) { 151 | String application = "application"; 152 | String projectPath = psiElement.getProject().getBasePath();//"D:\\project2\\test"; 153 | // String currentFilePath = psiElement.getContainingFile().getVirtualFile().getPath(); //"D:\\project2\\test\\application\\index\\controller\\Index.php"; 154 | String currentFilePath = psiElement.getContainingFile().getVirtualFile().getPath(); //"D:\\project2\\test\\project\\application\\index\\controller\\Index.php"; 155 | String[] arr = new String[0]; // project,application,index,xxx 156 | if (projectPath != null) { 157 | arr = currentFilePath.replace(projectPath, "").split("/"); 158 | } 159 | StringBuilder app = new StringBuilder(); 160 | 161 | for (int i = 0; i < arr.length; i++) { 162 | if (arr[i].equals(application)) { 163 | for (int j = 0; j < i; j++) { 164 | app.append(arr[j]).append("/"); 165 | } 166 | app.append(application); 167 | } 168 | } 169 | return app.toString(); 170 | } 171 | 172 | /** 173 | * @param psiElement 174 | * @return 当前文件名 175 | */ 176 | public static String getCurFileName(PsiElement psiElement) { 177 | return psiElement.getContainingFile().getVirtualFile().getName(); 178 | } 179 | 180 | 181 | public static String getKeyWithCase(Collection allKeys, String key) { 182 | for (String item : allKeys) { 183 | if (item.equals(key)) { 184 | return item; 185 | } 186 | } 187 | for (String item : allKeys) { 188 | if (item.toLowerCase().equals(key.toLowerCase())) { 189 | return item; 190 | } 191 | } 192 | return key; 193 | } 194 | 195 | public static Boolean isHintMethod(PsiElement param, MethodMatcher.CallToSignature[] Signatures, int paramIndex, boolean compareClass) { 196 | PsiElement methodRef = param.getParent().getParent(); 197 | compareClass = compareClass ? compareClass : false; 198 | if (!(methodRef instanceof MethodReference)) return false; 199 | String name = ((MethodReference) methodRef).getName(); 200 | List list = new ArrayList<>(); 201 | for (MethodMatcher.CallToSignature signature : Signatures) { 202 | String method = signature.getMethod(); 203 | if (method.equals(name)) { 204 | if (!compareClass) return PsiElementUtil.isFunctionReference(param, name, paramIndex); 205 | list.add(signature); 206 | } 207 | } 208 | if (list.size() == 0) return false; 209 | MethodMatcher.CallToSignature[] newSignatures = new MethodMatcher.CallToSignature[list.size()]; 210 | for (int i = 0; i < list.size(); i++) { 211 | newSignatures[i] = list.get(i); 212 | } 213 | boolean res = MethodMatcher.getMatchedSignatureWithDepth(param, newSignatures, paramIndex) != null; 214 | return res; 215 | } 216 | 217 | public static void setConfig(String settingfile) { 218 | Config config = null; 219 | try { 220 | String str = ""; 221 | File file = new File(settingfile);//定义一个file对象,用来初始化FileReader 222 | FileReader reader = new FileReader(file);//定义一个fileReader对象,用来初始化BufferedReader 223 | BufferedReader bReader = new BufferedReader(reader);//new一个BufferedReader对象,将文件内容读取到缓存 224 | StringBuilder sb = new StringBuilder();//定义一个字符串缓存,将字符串存放缓存中 225 | String s = ""; 226 | while ((s = bReader.readLine()) != null) {//逐行读取文件内容,不读取换行符和末尾的空格 227 | sb.append(s + "\r\n");//将读取的字符串添加换行符后累加存放在缓存中 228 | } 229 | bReader.close(); 230 | str = sb.toString(); 231 | ObjectMapper mapper = new ObjectMapper(); 232 | mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); 233 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 234 | config = mapper.readValue(str, Config.class); 235 | } catch (Exception e) { 236 | System.out.println("读取配置失败"); 237 | } 238 | if (config != null) Setting.config = config; 239 | } 240 | 241 | /** 242 | * 是否是配置的提示方法, 243 | */ 244 | public static boolean isConfigMethod(PsiElement element, int type) { 245 | String[] methodStrs; 246 | if (type == 1) { 247 | methodStrs = Setting.config.dbMethod; 248 | } else { 249 | methodStrs = Setting.config.dbArrMethod; 250 | } 251 | if (methodStrs == null) return false; 252 | for (String item : methodStrs) { 253 | int index = 0; 254 | String methodStr = item; 255 | if (item.contains(".")) { 256 | String[] method = item.split("\\."); 257 | if (method.length == 2) { 258 | try { 259 | index = Integer.valueOf(method[1]) - 1; 260 | } catch (Exception e) { 261 | } 262 | if (index < 0) index = 0; 263 | methodStr = method[0]; 264 | } 265 | } 266 | if (PsiElementUtil.isFunctionReference(element, methodStr, index)) { 267 | return true; 268 | } 269 | } 270 | return false; 271 | } 272 | 273 | public static String formatLog(String str){ 274 | if(str==null)return ""; 275 | str=str.replace("[","\r\n["); 276 | str=str.replace("]","]\r\n"); 277 | str=str.replace("\r\n \r\n","\r\n"); 278 | str=str.replace("', '","',\r\n'"); 279 | return str; 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/util/VfsExUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.util; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.vfs.VfsUtil; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author Daniel Espendiller 11 | */ 12 | public class VfsExUtil { 13 | public static String getRelativeProjectPath(@NotNull Project project, @NotNull VirtualFile virtualFile) { 14 | // hacking around project as temp file 15 | if(ApplicationManager.getApplication().isUnitTestMode()) { 16 | return virtualFile.getPath(); 17 | } 18 | return VfsUtil.getRelativePath(virtualFile, project.getBaseDir()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/view/GotoController.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.view; 2 | 3 | import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.actionSystem.DataContext; 7 | import com.intellij.openapi.editor.Editor; 8 | import com.intellij.psi.PsiElement; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public class GotoController extends AnAction { 12 | 13 | @Override 14 | public void actionPerformed(AnActionEvent e) { 15 | // TODO: insert action logic here 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/view/TemplateUtil.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.view; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.util.Pair; 5 | import com.intellij.openapi.util.text.StringUtil; 6 | import com.intellij.openapi.vfs.VfsUtil; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.PsiRecursiveElementWalkingVisitor; 11 | import com.jetbrains.php.lang.psi.elements.ClassReference; 12 | import com.jetbrains.php.lang.psi.elements.FunctionReference; 13 | import com.jetbrains.php.lang.psi.elements.MethodReference; 14 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 15 | import org.apache.commons.lang.StringUtils; 16 | import org.jetbrains.annotations.NotNull; 17 | import org.jetbrains.annotations.Nullable; 18 | import pers.fw.tplugin.view.dict.TemplatePath; 19 | 20 | import java.io.File; 21 | import java.util.*; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | public class TemplateUtil { 26 | public static Set RENDER_METHODS = new HashSet() {{ 27 | add("make"); 28 | add("of"); 29 | }}; 30 | 31 | @NotNull 32 | public static Set resolveTemplateName(@NotNull Project project, @NotNull String templateName) { 33 | Set templateNames = new HashSet<>(); 34 | 35 | int i = templateName.indexOf("::"); 36 | String ns = null; 37 | if (i > 0) { 38 | ns = templateName.substring(0, i); 39 | templateName = templateName.substring(i + 2, templateName.length()); 40 | } 41 | 42 | String pointName = templateName.replace(".", "/"); 43 | 44 | // find template files by extensions 45 | // templateNames.add(pointName.concat(".blade.php")); 46 | 47 | templateNames.add(ViewCollector.curModule + "/" + pointName.concat(".html")); 48 | templateNames.add(pointName.concat(".html")); 49 | 50 | Set templateFiles = new HashSet<>(); 51 | for (TemplatePath templatePath : ViewCollector.getPaths(project)) { 52 | // we have a namespace given; ignore all other paths 53 | String namespace = templatePath.getNamespace(); 54 | if ((ns == null && namespace != null) || ns != null && !ns.equals(namespace)) { 55 | continue; 56 | } 57 | 58 | VirtualFile viewDir = templatePath.getRelativePath(project); 59 | if (viewDir == null) { 60 | continue; 61 | } 62 | for (String templateRelative : templateNames) { 63 | VirtualFile viewsDir = VfsUtil.findRelativeFile(templateRelative, viewDir); 64 | if (viewsDir != null) { 65 | templateFiles.add(viewsDir); 66 | } 67 | } 68 | } 69 | return templateFiles; 70 | } 71 | 72 | @NotNull 73 | public static Collection resolveTemplateName(@NotNull PsiFile psiFile) { 74 | return resolveTemplateName(psiFile.getProject(), psiFile.getVirtualFile()); 75 | } 76 | 77 | @NotNull 78 | public static Collection resolveTemplateName(@NotNull Project project, @NotNull VirtualFile virtualFile) { 79 | Set templateNames = new HashSet<>(); 80 | 81 | for (TemplatePath templatePath : ViewCollector.getPaths(project)) { 82 | VirtualFile viewDir = templatePath.getRelativePath(project); 83 | if (viewDir == null) { 84 | continue; 85 | } 86 | 87 | String relativePath = VfsUtil.getRelativePath(virtualFile, viewDir); 88 | if (relativePath != null) { 89 | relativePath = stripTemplateExtensions(relativePath); 90 | 91 | if (templatePath.getNamespace() != null && StringUtils.isNotBlank(templatePath.getNamespace())) { 92 | templateNames.add(templatePath.getNamespace() + "::" + relativePath.replace("/", ".")); 93 | } else { 94 | templateNames.add(relativePath.replace("/", ".")); 95 | } 96 | } 97 | } 98 | 99 | return templateNames; 100 | } 101 | 102 | @NotNull 103 | public static Set resolveTemplateDirectory(@NotNull Project project, @NotNull String directory) { 104 | int i = directory.indexOf("::"); 105 | String ns = null; 106 | if (i > 0) { 107 | ns = directory.substring(0, i); 108 | directory = directory.substring(i + 2, directory.length()); 109 | } 110 | 111 | directory = directory.replace(".", "/"); 112 | 113 | Set templateFiles = new HashSet<>(); 114 | for (TemplatePath templatePath : ViewCollector.getPaths(project)) { 115 | // we have a namespace given; ignore all other paths 116 | String namespace = templatePath.getNamespace(); 117 | if ((ns == null && namespace != null) || ns != null && !ns.equals(namespace)) { 118 | continue; 119 | } 120 | 121 | VirtualFile viewDir = templatePath.getRelativePath(project); 122 | if (viewDir == null) { 123 | continue; 124 | } 125 | 126 | VirtualFile viewsDir = VfsUtil.findRelativeFile(directory, viewDir); 127 | if (viewsDir != null) { 128 | templateFiles.add(viewsDir); 129 | } 130 | } 131 | 132 | return templateFiles; 133 | } 134 | 135 | 136 | /** 137 | * Try to find directory or file navigation for template name 138 | *

139 | * "foo.bar" => "foo", "bar" 140 | */ 141 | @NotNull 142 | public static Collection resolveTemplate(@NotNull Project project, @NotNull String templateName, int offset) { 143 | Set files = new HashSet<>(); 144 | 145 | // try to find a path pattern on current offset after path normalization 146 | if (offset > 0 && offset < templateName.length()) { 147 | String templateNameWithCaret = normalizeTemplate(new StringBuilder(templateName).insert(offset, '\u0182').toString()).replace("/", "."); 148 | offset = templateNameWithCaret.indexOf('\u0182'); 149 | 150 | int i = StringUtils.strip(templateNameWithCaret.replace(String.valueOf('\u0182'), ""), "/").indexOf(".", offset); 151 | if (i > 0) { 152 | files.addAll(resolveTemplateDirectory(project, templateName.substring(0, i))); 153 | } 154 | } 155 | 156 | // full filepath fallback: "foo/foo.blade.php" 157 | if (files.size() == 0) { 158 | files.addAll(resolveTemplateName(project, templateName)); 159 | } 160 | 161 | return files; 162 | } 163 | 164 | /** 165 | * Normalize template path 166 | */ 167 | @NotNull 168 | public static String normalizeTemplate(@NotNull String templateName) { 169 | return templateName 170 | .replace("\\", "/") 171 | .replaceAll("/+", "/"); 172 | } 173 | 174 | 175 | /** 176 | * "'foobar'" 177 | * "'foobar', []" 178 | */ 179 | @Nullable 180 | public static String getParameterFromParameterDirective(@NotNull String content) { 181 | Matcher matcher = Pattern.compile("^\\s*['|\"]([^'\"]+)['|\"]").matcher(content); 182 | 183 | if (matcher.find()) { 184 | return StringUtil.trim(matcher.group(1)); 185 | } 186 | 187 | return null; 188 | } 189 | 190 | 191 | /** 192 | * Strip template extension for given template name 193 | *

194 | * "foo_blade.blade.php" => "foo_blade" 195 | * "foo_blade.html.twig" => "foo_blade" 196 | * "foo_blade.php" => "foo_blade" 197 | */ 198 | @NotNull 199 | public static String stripTemplateExtensions(@NotNull String filename) { 200 | if (filename.endsWith(".blade.php")) { 201 | filename = filename.substring(0, filename.length() - ".blade.php".length()); 202 | } else if (filename.endsWith(".html.twig")) { 203 | filename = filename.substring(0, filename.length() - ".html.twig".length()); 204 | } else if (filename.endsWith(".php")) { 205 | filename = filename.substring(0, filename.length() - ".php".length()); 206 | } 207 | 208 | return filename; 209 | } 210 | 211 | private static class MyViewRecursiveElementWalkingVisitor extends PsiRecursiveElementWalkingVisitor { 212 | private final Collection> views; 213 | 214 | private MyViewRecursiveElementWalkingVisitor(Collection> views) { 215 | this.views = views; 216 | } 217 | 218 | @Override 219 | public void visitElement(PsiElement element) { 220 | 221 | if (element instanceof MethodReference) { 222 | visitMethodReference((MethodReference) element); 223 | } 224 | 225 | if (element instanceof FunctionReference) { 226 | visitFunctionReference((FunctionReference) element); 227 | } 228 | 229 | super.visitElement(element); 230 | } 231 | 232 | private void visitFunctionReference(FunctionReference functionReference) { 233 | 234 | if (!"view".equals(functionReference.getName())) { 235 | return; 236 | } 237 | 238 | PsiElement[] parameters = functionReference.getParameters(); 239 | 240 | if (parameters.length < 1 || !(parameters[0] instanceof StringLiteralExpression)) { 241 | return; 242 | } 243 | 244 | String contents = ((StringLiteralExpression) parameters[0]).getContents(); 245 | if (StringUtils.isBlank(contents)) { 246 | return; 247 | } 248 | 249 | views.add(Pair.create(contents, parameters[0])); 250 | } 251 | 252 | private void visitMethodReference(MethodReference methodReference) { 253 | 254 | String methodName = methodReference.getName(); 255 | if (!RENDER_METHODS.contains(methodName)) { 256 | return; 257 | } 258 | 259 | PsiElement classReference = methodReference.getFirstChild(); 260 | if (!(classReference instanceof ClassReference)) { 261 | return; 262 | } 263 | 264 | if (!"View".equals(((ClassReference) classReference).getName())) { 265 | return; 266 | } 267 | 268 | PsiElement[] parameters = methodReference.getParameters(); 269 | if (parameters.length == 0 || !(parameters[0] instanceof StringLiteralExpression)) { 270 | return; 271 | } 272 | 273 | String contents = ((StringLiteralExpression) parameters[0]).getContents(); 274 | if (StringUtils.isBlank(contents)) { 275 | return; 276 | } 277 | 278 | views.add(Pair.create(contents, parameters[0])); 279 | } 280 | } 281 | 282 | public static File recursionMatch(File root, String file) { 283 | if (root == null) return null; 284 | File[] files = root.listFiles(); 285 | if (files != null) 286 | for (File f : files) { 287 | if (f.isFile()) { 288 | String curPath = f.getAbsolutePath().toLowerCase(); 289 | curPath = curPath.replace("\\", "/"); 290 | curPath=curPath.substring(0,curPath.lastIndexOf(".")); 291 | if (curPath.equals(file.toLowerCase())) { 292 | return f; 293 | } 294 | } else if (f.isDirectory()) { 295 | File res=recursionMatch(f, file); 296 | if(res!=null) 297 | return res; 298 | } 299 | } 300 | return null; 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/view/ViewCollector.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.view; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonIOException; 5 | import com.google.gson.JsonSyntaxException; 6 | import com.intellij.ide.highlighter.HtmlFileType; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.vfs.VfsUtil; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import com.intellij.openapi.vfs.VirtualFileVisitor; 11 | import com.intellij.psi.PsiDirectory; 12 | import com.intellij.psi.PsiFile; 13 | import com.intellij.psi.search.FilenameIndex; 14 | import com.intellij.psi.search.GlobalSearchScope; 15 | 16 | import com.intellij.psi.util.CachedValueProvider; 17 | import com.intellij.psi.util.CachedValuesManager; 18 | 19 | import org.apache.commons.lang.StringUtils; 20 | import org.jetbrains.annotations.NotNull; 21 | import org.jetbrains.annotations.Nullable; 22 | import pers.fw.tplugin.util.VfsExUtil; 23 | import pers.fw.tplugin.view.dict.JsonTemplatePaths; 24 | import pers.fw.tplugin.view.dict.TemplatePath; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.Collection; 29 | 30 | /** 31 | * @author Daniel Espendiller 32 | */ 33 | public class ViewCollector { 34 | /** 35 | * Default "view" path based on laravel versions 36 | */ 37 | public static String curModule = ""; 38 | public static TemplatePath[] DEFAULT_TEMPLATE_PATH; 39 | 40 | @NotNull 41 | public static Collection getPaths(@NotNull Project project) { 42 | return getPaths(project, false, false); //fowModify: true,true->false.false 43 | } 44 | 45 | @NotNull 46 | public static Collection getPaths(@NotNull Project project, boolean includeSettings, boolean includeJson) { 47 | Collection templatePaths = new ArrayList<>(Arrays.asList(DEFAULT_TEMPLATE_PATH)); 48 | return templatePaths; 49 | } 50 | 51 | private static void collectIdeJsonBladePaths(@NotNull Project project, @NotNull Collection templatePaths) { 52 | for (PsiFile psiFile : FilenameIndex.getFilesByName(project, "ide-blade.json", GlobalSearchScope.allScope(project))) { 53 | Collection cachedValue = CachedValuesManager.getCachedValue(psiFile, new MyJsonCachedValueProvider(psiFile)); 54 | if (cachedValue != null) { 55 | templatePaths.addAll(cachedValue); 56 | } 57 | } 58 | } 59 | 60 | /** 61 | * Visit all templates in project path configuration 62 | */ 63 | public static void visitFile(@NotNull Project project, @NotNull ViewVisitor visitor) { 64 | //fowModify 65 | // for(TemplatePath templatePath : getPaths(project)) { 66 | Collection paths = getPaths(project, false, false); 67 | for (TemplatePath templatePath : paths) { 68 | visitTemplatePath(project, templatePath, visitor); 69 | } 70 | } 71 | 72 | /** 73 | * Visit all templates in given path 74 | */ 75 | private static void visitTemplatePath(@NotNull Project project, @NotNull TemplatePath templatePath, @NotNull ViewVisitor visitor) { 76 | final VirtualFile templateDir = VfsUtil.findRelativeFile(templatePath.getPath(), project.getBaseDir()); 77 | if (templateDir == null) { 78 | return; 79 | } 80 | 81 | VfsUtil.visitChildrenRecursively(templateDir, new VirtualFileVisitor() { 82 | @Override 83 | public boolean visitFile(@NotNull VirtualFile virtualFile) { 84 | // if (virtualFile.isDirectory() || !isTemplateFile(virtualFile)) { 85 | //修改, 不匹配后缀,view下的文件全为视图文件 86 | if (virtualFile.isDirectory()) { 87 | return true; 88 | } 89 | 90 | String filename = VfsUtil.getRelativePath(virtualFile, templateDir, '/'); 91 | if (filename == null || !filename.contains("/")) { 92 | return true; 93 | } 94 | //fowModify: 95 | // filename = BladeTemplateUtil.stripTemplateExtensions(filename); 96 | // filename = clearStr(filename); 97 | filename = filename.substring(0, filename.lastIndexOf("."));//去掉后缀 98 | String namespace = templatePath.getNamespace(); 99 | if (namespace != null && StringUtils.isNotBlank(namespace)) { 100 | visitor.visit(virtualFile, namespace + "::" + filename); 101 | } else { 102 | visitor.visit(virtualFile, filename); 103 | } 104 | 105 | return true; 106 | } 107 | 108 | 109 | /** 110 | * 清理多余字符, 111 | */ 112 | private String clearStr(String filename) { 113 | filename = filename.substring(0, filename.length() - ".html".length()); 114 | //如果没有module 115 | String prefix = filename.substring(0, curModule.length()); 116 | if (curModule.equals(prefix)) { 117 | int len = filename.length(); 118 | filename = filename.substring(curModule.length() + 1, len); 119 | } 120 | return filename; 121 | } 122 | 123 | private boolean isTemplateFile(VirtualFile virtualFile) { 124 | // if(virtualFile.getFileType() == BladeFileType.INSTANCE || virtualFile.getFileType() == PhpFileType.INSTANCE) { 125 | if (virtualFile.getFileType() == HtmlFileType.INSTANCE) { 126 | return true; 127 | } 128 | 129 | String extension = virtualFile.getExtension(); 130 | // if (extension != null && (extension.equalsIgnoreCase("php") || extension.equalsIgnoreCase("twig"))) { 131 | if (extension != null && (extension.equalsIgnoreCase("html") 132 | || extension.equalsIgnoreCase("php") 133 | || extension.equalsIgnoreCase("tpl") 134 | )) { 135 | return true; 136 | } 137 | 138 | return false; 139 | } 140 | }); 141 | } 142 | 143 | public interface ViewVisitor { 144 | void visit(@NotNull VirtualFile virtualFile, @NotNull String name); 145 | } 146 | 147 | private static class MyJsonCachedValueProvider implements CachedValueProvider> { 148 | private final PsiFile psiFile; 149 | 150 | public MyJsonCachedValueProvider(PsiFile psiFile) { 151 | this.psiFile = psiFile; 152 | } 153 | 154 | @Nullable 155 | @Override 156 | public Result> compute() { 157 | 158 | Collection twigPaths = new ArrayList<>(); 159 | 160 | String text = psiFile.getText(); 161 | JsonTemplatePaths configJson = null; 162 | try { 163 | configJson = new Gson().fromJson(text, JsonTemplatePaths.class); 164 | } catch (JsonSyntaxException | JsonIOException | IllegalStateException ignored) { 165 | } 166 | 167 | if (configJson == null) { 168 | return Result.create(twigPaths, psiFile, psiFile.getVirtualFile()); 169 | } 170 | 171 | Collection namespaces = configJson.getNamespaces(); 172 | if (namespaces == null || namespaces.size() == 0) { 173 | return Result.create(twigPaths, psiFile, psiFile.getVirtualFile()); 174 | } 175 | 176 | for (JsonTemplatePaths.Path jsonPath : namespaces) { 177 | String path = jsonPath.getPath(); 178 | if (path == null) { 179 | path = ""; 180 | } 181 | 182 | path = StringUtils.stripStart(path.replace("\\", "/"), "/"); 183 | PsiDirectory parent = psiFile.getParent(); 184 | if (parent == null) { 185 | continue; 186 | } 187 | 188 | // current directory check and subfolder 189 | VirtualFile twigRoot; 190 | if (path.length() > 0) { 191 | twigRoot = VfsUtil.findRelativeFile(parent.getVirtualFile(), path.split("/")); 192 | } else { 193 | twigRoot = psiFile.getParent().getVirtualFile(); 194 | } 195 | 196 | if (twigRoot == null) { 197 | continue; 198 | } 199 | 200 | String relativePath = VfsExUtil.getRelativeProjectPath(psiFile.getProject(), twigRoot); 201 | if (relativePath == null) { 202 | continue; 203 | } 204 | 205 | String namespace = jsonPath.getNamespace(); 206 | 207 | String namespacePath = StringUtils.stripStart(relativePath, "/"); 208 | 209 | if (StringUtils.isNotBlank(namespace)) { 210 | twigPaths.add(new TemplatePath(namespacePath, namespace, true)); 211 | } else { 212 | twigPaths.add(new TemplatePath(namespacePath, true)); 213 | } 214 | } 215 | 216 | return Result.create(twigPaths, psiFile, psiFile.getVirtualFile()); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/view/ViewReferences2.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.view; 2 | 3 | import com.intellij.codeInsight.lookup.LookupElement; 4 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 5 | import com.intellij.lang.Language; 6 | import com.intellij.openapi.application.ApplicationManager; 7 | import com.intellij.openapi.editor.Editor; 8 | import com.intellij.openapi.vfs.VfsUtil; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import com.intellij.patterns.PlatformPatterns; 11 | import com.intellij.psi.PsiElement; 12 | import com.jetbrains.php.lang.PhpLanguage; 13 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | import pers.fw.tplugin.inter.GotoCompletionContributor; 17 | import pers.fw.tplugin.inter.GotoCompletionLanguageRegistrar; 18 | import pers.fw.tplugin.inter.GotoCompletionProvider; 19 | import pers.fw.tplugin.inter.GotoCompletionRegistrarParameter; 20 | import pers.fw.tplugin.util.MethodMatcher; 21 | import pers.fw.tplugin.util.PsiElementUtil; 22 | import pers.fw.tplugin.util.PsiElementUtils; 23 | import pers.fw.tplugin.util.Util; 24 | import pers.fw.tplugin.view.dict.TemplatePath; 25 | import java.io.File; 26 | import java.util.ArrayList; 27 | import java.util.Collection; 28 | import java.util.Collections; 29 | 30 | // view 处理类 31 | public class ViewReferences2 implements GotoCompletionLanguageRegistrar { 32 | private static MethodMatcher.CallToSignature[] VIEWS = new MethodMatcher.CallToSignature[]{ 33 | new MethodMatcher.CallToSignature("\\think\\View", "fetch"), 34 | new MethodMatcher.CallToSignature("\\think\\Controller", "fetch"), 35 | new MethodMatcher.CallToSignature("\\think\\Controller", "display"), 36 | }; 37 | 38 | @Override 39 | public void register(GotoCompletionRegistrarParameter registrar) { 40 | registrar.register(PlatformPatterns.psiElement(), new GotoCompletionContributor() { 41 | @Nullable 42 | @Override 43 | public GotoCompletionProvider getProvider(@Nullable PsiElement psiElement) { 44 | if (psiElement == null) {// || !LaravelProjectComponent.isEnabled(psiElement)) { 45 | return null; 46 | } 47 | 48 | PsiElement parent = psiElement.getParent(); 49 | if (parent != null 50 | && (PsiElementUtil.isFunctionReference(parent, "view", 0) || MethodMatcher.getMatchedSignatureWithDepth(parent, VIEWS) != null) 51 | && handlePath(psiElement)) { 52 | return new ViewDirectiveCompletionProvider(parent); 53 | } 54 | return null; 55 | } 56 | }); 57 | } 58 | 59 | 60 | @Override 61 | public boolean support(@NotNull Language language) { 62 | return PhpLanguage.INSTANCE == language; 63 | } 64 | 65 | public boolean handlePath(PsiElement psiElement) { 66 | // todo ensure base dir 67 | // String application = "application"; 68 | // String projectPath = psiElement.getProject().getBasePath();//"D:\\project2\\test"; 69 | // String currentFilePath = psiElement.getContainingFile().getVirtualFile().getPath(); //"D:\\project2\\test\\application\\index\\controller\\Index.php"; 70 | // String[] arr = currentFilePath.replace(projectPath, "").split("/"); 71 | // if (arr.length < 4 || !arr[1].equals(application)) { 72 | // return false; 73 | // } 74 | String application = Util.getApplicationDir(psiElement); 75 | // String moduleName = arr[2]; 76 | String moduleName = Util.getCurTpModuleName(psiElement); 77 | ViewCollector.DEFAULT_TEMPLATE_PATH = new TemplatePath[]{new TemplatePath(application + "/" + moduleName + "/view", false)}; 78 | ViewCollector.curModule = moduleName; 79 | return true; 80 | } 81 | 82 | 83 | private static class ViewDirectiveCompletionProvider extends GotoCompletionProvider { 84 | private ViewDirectiveCompletionProvider(PsiElement element) { 85 | super(element); 86 | } 87 | 88 | @NotNull 89 | @Override 90 | public Collection getLookupElements() { 91 | 92 | final Collection lookupElements = new ArrayList<>(); 93 | 94 | ViewCollector.visitFile(getProject(), new ViewCollector.ViewVisitor() { 95 | @Override 96 | public void visit(@NotNull VirtualFile virtualFile, @NotNull String name) { 97 | lookupElements.add(LookupElementBuilder.create(name).withIcon(virtualFile.getFileType().getIcon())); 98 | } 99 | } 100 | ); 101 | 102 | // @TODO: no filesystem access in test; fake item 103 | if (ApplicationManager.getApplication().isUnitTestMode()) { 104 | lookupElements.add(LookupElementBuilder.create("test_view")); 105 | } 106 | 107 | return lookupElements; 108 | } 109 | 110 | @NotNull 111 | @Override 112 | public Collection getPsiTargets(@NotNull PsiElement psiElement, int offset, @NotNull Editor editor) { 113 | PsiElement stringLiteral = psiElement.getParent(); 114 | if (!(stringLiteral instanceof StringLiteralExpression)) { 115 | return Collections.emptyList(); 116 | } 117 | 118 | String contents = ((StringLiteralExpression) stringLiteral).getContents(); 119 | 120 | String viewDir = getProject().getBaseDir() + Util.getApplicationDir(psiElement) + "/" + Util.getCurTpModuleName(psiElement) + "/" + "view" + "/"; 121 | String className = ""; 122 | String methodName = ""; 123 | 124 | if (contents.contains("/")) { // index/test 125 | String[] split = contents.split("/"); 126 | className = split[0]; 127 | methodName = contents.substring(className.length() + 1); 128 | } else if ("".equals(contents)) { // 129 | className = Util.getPhpClass(psiElement).getName().toLowerCase(); 130 | methodName = Util.getMethod(psiElement).getName(); 131 | } else { // test 132 | className = Util.getPhpClass(psiElement).getName().toLowerCase(); 133 | methodName = contents; 134 | } 135 | 136 | String dir = viewDir + className;//+ "/" + methodName; 137 | String filePath = viewDir + className + "/" + methodName; 138 | dir = dir.replace("file://", ""); 139 | filePath = filePath.replace("file://", ""); 140 | File file = new File(dir); 141 | File targetFile = TemplateUtil.recursionMatch(file, filePath); 142 | Collection virFiles = new ArrayList<>(); 143 | if (targetFile != null) { 144 | VirtualFile fileByIoFile = VfsUtil.findFileByIoFile(targetFile, false); 145 | virFiles.add(fileByIoFile); 146 | } 147 | Collection targets = new ArrayList<>(PsiElementUtils.convertVirtualFilesToPsiFiles(getProject(), virFiles)); 148 | 149 | return targets; 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/view/dict/JsonTemplatePaths.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.view.dict; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | 8 | /** 9 | * @author Daniel Espendiller 10 | */ 11 | public class JsonTemplatePaths { 12 | @Nullable 13 | private Collection namespaces = new ArrayList<>(); 14 | 15 | @Nullable 16 | public Collection getNamespaces() { 17 | return namespaces; 18 | } 19 | 20 | public static class Path { 21 | @Nullable 22 | private String path; 23 | 24 | @Nullable 25 | private String namespace; 26 | 27 | @Nullable 28 | public String getPath() { 29 | return path; 30 | } 31 | 32 | @Nullable 33 | public String getNamespace() { 34 | return namespace; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/pers/fw/tplugin/view/dict/TemplatePath.java: -------------------------------------------------------------------------------- 1 | package pers.fw.tplugin.view.dict; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.vfs.VfsUtil; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import com.intellij.util.xmlb.annotations.Attribute; 7 | import com.intellij.util.xmlb.annotations.Tag; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.io.File; 12 | 13 | /** 14 | * @author Daniel Espendiller 15 | */ 16 | @Tag("templatePath") 17 | public class TemplatePath { 18 | 19 | private String path; 20 | private boolean customPath = true; 21 | 22 | @Nullable 23 | private String namespace; 24 | 25 | public TemplatePath() { 26 | } 27 | 28 | public TemplatePath(@NotNull String path, boolean customPath) { 29 | this.path = path; 30 | this.customPath = customPath; 31 | } 32 | 33 | public TemplatePath(@NotNull String path, @Nullable String namespace, boolean customPath) { 34 | this(path, customPath); 35 | this.namespace = namespace; 36 | } 37 | 38 | @Attribute("path") 39 | public String getPath() { 40 | return path; 41 | } 42 | 43 | @Nullable 44 | @Attribute("namespace") 45 | public String getNamespace() { 46 | return namespace; 47 | } 48 | 49 | @Override 50 | public TemplatePath clone() { 51 | 52 | try { 53 | super.clone(); 54 | } catch (CloneNotSupportedException ignored) { 55 | } 56 | 57 | return new TemplatePath(this.getPath(), this.getNamespace(), this.isCustomPath()); 58 | } 59 | 60 | public boolean isCustomPath() { 61 | return customPath; 62 | } 63 | 64 | @Nullable 65 | public VirtualFile getRelativePath(Project project) { 66 | return VfsUtil.findRelativeFile(project.getBaseDir(), this.getPath().split("/")); 67 | } 68 | 69 | public VirtualFile getDirectory() { 70 | 71 | File file = new File(this.getPath()); 72 | 73 | if(!file.exists()) { 74 | return null; 75 | } 76 | 77 | return VfsUtil.findFileByIoFile(file, true); 78 | } 79 | 80 | public void setPath(String path) { 81 | this.path = path; 82 | } 83 | 84 | public void setNamespace(@Nullable String namespace) { 85 | this.namespace = namespace; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tplugin.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | //数据库字段提示方法(方法名.参数位置), 参数可不设置, 默认为第一个参数, test("字段提示") 4 | "dbMethod": [ 5 | "test", 6 | //示例 7 | "save.2" 8 | ], 9 | //数据库字段提示方法, 数据参数, test(["字段提示"=>100]) 10 | "dbArrMethod": [ 11 | "test" 12 | ], 13 | //数据库提示字段, res["数据库字段"]=100 14 | "dbVar": [ 15 | "res", 16 | "map", 17 | "where", 18 | "data", 19 | "this->data" 20 | ], 21 | 22 | //日志开关,不设置默认关闭,该设置重启软件生效 23 | "logEnable": false, 24 | //通过前缀筛选日志,//最好从日志文件中复制前缀,如[ sql ] [ SQL ] 25 | "logPrefix": [ 26 | "[ info ] [ PARAM ]" 27 | ], 28 | //通过正则筛选日志 29 | "logRegex":[ 30 | "^.*\\[\\sSQL ((?!COLUMNS).)*$" //显示带有[ sql但没是COLUMNS的记录 31 | ] 32 | } --------------------------------------------------------------------------------