├── settings.gradle ├── src └── main │ ├── resources │ ├── icons │ │ ├── help.png │ │ ├── regex.png │ │ ├── help@2x.png │ │ ├── help_dark.png │ │ ├── regex_dark.png │ │ ├── help@2x_dark.png │ │ ├── selfbuild.svg │ │ ├── help.svg │ │ ├── help_dark.svg │ │ └── regex_dark.svg │ ├── META-INF │ │ └── plugin.xml │ └── data │ │ └── rule.js │ └── java │ └── cn │ └── olange │ ├── model │ ├── Handler.java │ ├── AsyncResult.java │ ├── RuleTableCellRender.java │ ├── Config.java │ ├── MyEnterAction.java │ ├── RegExTableModel.java │ ├── UsageTableCellRenderer.java │ └── RuleModel.java │ ├── utils │ ├── RuleUtil.java │ └── HttpUtil.java │ ├── AnyRuleFindAction.java │ ├── setting │ ├── action │ │ ├── DeleteRuleAction.java │ │ └── NewRuleAction.java │ ├── SettingConfigurable.java │ ├── RulePersistentConfig.java │ ├── RuleFormDialog.java │ ├── RuleFormDialog.form │ ├── SettingUI.form │ └── SettingUI.java │ ├── service │ └── RuleDataService.java │ └── ui │ ├── RulePreviewPanel.java │ └── AnyRulePopupPanel.java ├── CHANGELOG.txt ├── .gitignore ├── LICENSE ├── README.md ├── .github └── workflows │ ├── gradle-publish.yml │ └── gradle.yml ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "any-rule" 2 | -------------------------------------------------------------------------------- /src/main/resources/icons/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linindoo/idea-rule/HEAD/src/main/resources/icons/help.png -------------------------------------------------------------------------------- /src/main/resources/icons/regex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linindoo/idea-rule/HEAD/src/main/resources/icons/regex.png -------------------------------------------------------------------------------- /src/main/resources/icons/help@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linindoo/idea-rule/HEAD/src/main/resources/icons/help@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/help_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linindoo/idea-rule/HEAD/src/main/resources/icons/help_dark.png -------------------------------------------------------------------------------- /src/main/resources/icons/regex_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linindoo/idea-rule/HEAD/src/main/resources/icons/regex_dark.png -------------------------------------------------------------------------------- /src/main/resources/icons/help@2x_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Linindoo/idea-rule/HEAD/src/main/resources/icons/help@2x_dark.png -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/Handler.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | @FunctionalInterface 4 | public interface Handler { 5 | void handle(E var1); 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | Version 1.0.2 2 | * [fix] 修复数组越界的bug 3 | * [update] 正则数据同步更新 4 | * [update] 默认选中搜索输入框,优化使用体验 5 | Version 1.0.1 6 | * [add] 支持快捷键和鼠标右键两种调取工具方式 7 | * [add] 支持模糊查询 8 | * [add] 可以选中合适的正则直接添加到文本编辑器中 9 | * [add] 支持正则预览 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # idea 26 | .idea 27 | any-rule.iml 28 | any-rule.jar 29 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/utils/RuleUtil.java: -------------------------------------------------------------------------------- 1 | package cn.olange.utils; 2 | 3 | public class RuleUtil { 4 | 5 | public static String convertRule(String rule) { 6 | if (rule == null) { 7 | return ""; 8 | } 9 | int first = rule.indexOf("/"); 10 | first = Math.max(first + 1, 0); 11 | int lastIndexOf = rule.lastIndexOf("/"); 12 | lastIndexOf = Math.min(lastIndexOf, rule.length() -1); 13 | return rule.substring(first, lastIndexOf).replace("[\\]", ""); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/AsyncResult.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | public class AsyncResult { 4 | private boolean success; 5 | private T result; 6 | 7 | public AsyncResult(boolean success, T result) { 8 | this.success = success; 9 | this.result = result; 10 | } 11 | 12 | public boolean isSuccess() { 13 | return success; 14 | } 15 | 16 | public void setSuccess(boolean success) { 17 | this.success = success; 18 | } 19 | 20 | public T getResult() { 21 | return result; 22 | } 23 | 24 | public void setResult(T result) { 25 | this.result = result; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/AnyRuleFindAction.java: -------------------------------------------------------------------------------- 1 | package cn.olange; 2 | 3 | import cn.olange.ui.AnyRulePopupPanel; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.actionSystem.CommonDataKeys; 7 | import com.intellij.openapi.editor.Editor; 8 | 9 | public class AnyRuleFindAction extends AnAction { 10 | 11 | @Override 12 | public void actionPerformed(AnActionEvent e) { 13 | Editor editor = e.getData(CommonDataKeys.EDITOR); 14 | AnyRulePopupPanel popupPanel = new AnyRulePopupPanel(e.getProject(), editor); 15 | popupPanel.showUI(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/RuleTableCellRender.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | import com.intellij.ui.ColoredTableCellRenderer; 4 | import com.intellij.ui.SimpleTextAttributes; 5 | 6 | import javax.swing.*; 7 | 8 | public class RuleTableCellRender extends ColoredTableCellRenderer { 9 | 10 | @Override 11 | protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { 12 | if (value instanceof RuleModel) { 13 | RuleModel ruleObj = (RuleModel) value; 14 | this.append(" " + ruleObj.getRule(), SimpleTextAttributes.SYNTHETIC_ATTRIBUTES); 15 | } 16 | setBorder(null); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/Config.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | 4 | import java.util.List; 5 | 6 | public class Config { 7 | private String updateUrl ="https://raw.githubusercontent.com/any86/any-rule/v0.3.7/packages/www/src/RULES.js"; 8 | private List regExpList; 9 | 10 | public String getUpdateUrl() { 11 | return updateUrl; 12 | } 13 | 14 | public void setUpdateUrl(String updateUrl) { 15 | this.updateUrl = updateUrl; 16 | } 17 | 18 | public List getRegExpList() { 19 | return regExpList; 20 | } 21 | 22 | public void setRegExpList(List regExpList) { 23 | this.regExpList = regExpList; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/action/DeleteRuleAction.java: -------------------------------------------------------------------------------- 1 | package cn.olange.setting.action; 2 | 3 | import cn.olange.setting.SettingUI; 4 | import com.intellij.icons.AllIcons; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.project.DumbAwareAction; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class DeleteRuleAction extends DumbAwareAction { 10 | 11 | 12 | private SettingUI settingUI; 13 | 14 | public DeleteRuleAction(SettingUI settingUI) { 15 | super("删除规则", "", AllIcons.Actions.Cancel); 16 | this.settingUI = settingUI; 17 | } 18 | 19 | @Override 20 | public void actionPerformed(@NotNull AnActionEvent anActionEvent) { 21 | settingUI.deleteSelectRow(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/icons/selfbuild.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/MyEnterAction.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | import cn.olange.ui.AnyRulePopupPanel; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.CommonDataKeys; 6 | import com.intellij.openapi.project.DumbAwareAction; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class MyEnterAction extends DumbAwareAction { 10 | 11 | private AnyRulePopupPanel anyRulePopupPanel; 12 | 13 | public MyEnterAction(AnyRulePopupPanel anyRulePopupPanel) { 14 | this.anyRulePopupPanel = anyRulePopupPanel; 15 | } 16 | 17 | public void update(@NotNull AnActionEvent e) { 18 | e.getPresentation().setEnabled(e.getData(CommonDataKeys.EDITOR) == null); 19 | } 20 | 21 | public void actionPerformed(@NotNull AnActionEvent e) { 22 | anyRulePopupPanel.insertRuleToDocument(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/action/NewRuleAction.java: -------------------------------------------------------------------------------- 1 | package cn.olange.setting.action; 2 | 3 | import cn.olange.setting.RuleFormDialog; 4 | import cn.olange.setting.SettingUI; 5 | import com.intellij.icons.AllIcons; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import com.intellij.openapi.project.DumbAwareAction; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class NewRuleAction extends DumbAwareAction { 11 | 12 | 13 | private SettingUI settingUI; 14 | 15 | public NewRuleAction(SettingUI settingUI) { 16 | super("添加规则", "", AllIcons.General.Add); 17 | this.settingUI = settingUI; 18 | } 19 | 20 | @Override 21 | public void actionPerformed(@NotNull AnActionEvent anActionEvent) { 22 | RuleFormDialog ruleFormDialog = new RuleFormDialog(anActionEvent.getProject(), settingUI, null); 23 | ruleFormDialog.show(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Olange 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # idea-rule 2 | 3 | 基于IDEA平台的常用正则表达式插件 4 | 5 | ### 安装 6 | 7 | IDEA应用商店中搜索"**any-rule**". 8 | 9 | ### 使用 10 | **方式1:** 11 | 12 | 右键选择**Any Rule** 打开正则列表 13 | 14 | ![](http://www.52zhoujia.cn/upload/2020/04/6j73mdhsj4g4aqpvrv23af8atv.gif) 15 | **方式2:** 16 | 17 | 按**alt + a**打开正则列表 18 | 19 | ![](http://www.52zhoujia.cn/upload/2020/04/o8ks49pfnmhisq30bmpt9obpb3.gif) 20 | ### 本地添加自定义正则 21 | ![image](https://user-images.githubusercontent.com/26195410/141395383-ba5715d5-665b-445c-982a-91c6dcfbcca2.png) 22 | ### 更新在线正则表达式 23 | 这次,自定义正则不会再被覆盖了,可以放心更新 24 | 不过由于github访问的问题,国内不一定能够拉取到,可以使用这个cdn加速地址来更新 [https://www.52zhoujia.cn/any-rule/packages/www/src/RULES.js](https://www.52zhoujia.cn/any-rule/packages/www/src/RULES.js) 25 | 26 | ## :fire:关于插件 27 | 数据来源于[anyrule](https://github.com/any86/any-rule/) 28 | 29 | ## 鸣谢 30 | 31 | [![image](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png?_ga=2.24944058.1290266934.1645349446-1969556127.1639968575&_gl=1*1wtez8q*_ga*MTk2OTU1NjEyNy4xNjM5OTY4NTc1*_ga_V0XZL7QHEB*MTY0NTM0OTQ4My4xMS4wLjE2NDUzNDk0ODMuMA..)](https://jb.gg/OpenSourceSupport) 32 | 33 | 最后,感谢 `JetBrains` 提供了优秀的开发工具 34 | -------------------------------------------------------------------------------- /src/main/resources/icons/help.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/icons/help_dark.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/RegExTableModel.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | import javax.swing.table.AbstractTableModel; 4 | import java.util.List; 5 | 6 | public class RegExTableModel extends AbstractTableModel { 7 | private String[] heards = new String[]{"规则名称", "规则", "示例"}; 8 | 9 | private List ruleModels; 10 | 11 | public RegExTableModel(List ruleModels) { 12 | 13 | this.ruleModels = ruleModels; 14 | } 15 | 16 | @Override 17 | public int getRowCount() { 18 | return ruleModels.size(); 19 | } 20 | 21 | @Override 22 | public int getColumnCount() { 23 | return heards.length; 24 | } 25 | 26 | @Override 27 | public String getColumnName(int columnIndex) { 28 | return heards[columnIndex]; 29 | } 30 | 31 | @Override 32 | public Object getValueAt(int rowIndex, int columnIndex) { 33 | RuleModel ruleModel = this.ruleModels.get(rowIndex); 34 | if (columnIndex == 0) { 35 | return ruleModel.getTitle(); 36 | } else if (columnIndex == 1) { 37 | return ruleModel.getRule(); 38 | } else if (columnIndex == 2) { 39 | return ruleModel.getExamples(); 40 | } 41 | return ""; 42 | } 43 | 44 | @Override 45 | public boolean isCellEditable(int rowIndex, int columnIndex) { 46 | return false; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/UsageTableCellRenderer.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | import com.intellij.ui.ColoredTableCellRenderer; 4 | import com.intellij.ui.components.JBLabel; 5 | import com.intellij.util.ui.JBUI; 6 | 7 | import javax.swing.*; 8 | import javax.swing.table.TableCellRenderer; 9 | import java.awt.*; 10 | 11 | public class UsageTableCellRenderer extends JPanel implements TableCellRenderer { 12 | private static final int MARGIN = 2; 13 | private JBLabel title; 14 | private final ColoredTableCellRenderer myRule; 15 | 16 | 17 | public UsageTableCellRenderer() { 18 | setLayout(new BorderLayout()); 19 | title = new JBLabel(); 20 | myRule = new RuleTableCellRender(); 21 | add(myRule, BorderLayout.CENTER); 22 | add(title, BorderLayout.WEST); 23 | setBorder(JBUI.Borders.empty(MARGIN, MARGIN, MARGIN, 0)); 24 | } 25 | 26 | @Override 27 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 28 | myRule.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 29 | Color color = isSelected ? table.getSelectionBackground() : table.getBackground(); 30 | setBackground(color); 31 | title.setBackground(color); 32 | myRule.setBackground(color); 33 | if (value instanceof RuleModel) { 34 | RuleModel ruleObj = (RuleModel) value; 35 | this.title.setText(ruleObj.getTitle()); 36 | } 37 | return this; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/SettingConfigurable.java: -------------------------------------------------------------------------------- 1 | package cn.olange.setting; 2 | 3 | import com.intellij.openapi.options.ConfigurationException; 4 | import com.intellij.openapi.options.SearchableConfigurable; 5 | import com.intellij.openapi.util.NlsContexts; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import javax.swing.*; 10 | 11 | public class SettingConfigurable implements SearchableConfigurable { 12 | public static final String DISPLAY_NAME = "any rule"; 13 | 14 | private SettingUI mainPanel; 15 | @NotNull 16 | @Override 17 | public String getId() { 18 | return "any.rule"; 19 | } 20 | 21 | @Override 22 | public String getDisplayName() { 23 | return DISPLAY_NAME; 24 | } 25 | 26 | 27 | @Nullable 28 | @Override 29 | public JComponent createComponent() { 30 | mainPanel = new SettingUI(); 31 | return mainPanel.getComponent(); 32 | } 33 | 34 | @Override 35 | public boolean isModified() { 36 | return mainPanel.isModified(); 37 | } 38 | 39 | @Override 40 | public void apply() throws ConfigurationException { 41 | mainPanel.apply(); 42 | } 43 | 44 | @Override 45 | public void reset() { 46 | mainPanel.reset(); 47 | } 48 | 49 | @Override 50 | public void disposeUIResources() { 51 | // mainPanel.disposeUIResources(); 52 | mainPanel = null; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/model/RuleModel.java: -------------------------------------------------------------------------------- 1 | package cn.olange.model; 2 | 3 | public class RuleModel { 4 | private String title; 5 | private String rule; 6 | private String examples; 7 | private boolean selfBuild; 8 | 9 | public String getTitle() { 10 | return title; 11 | } 12 | 13 | public void setTitle(String title) { 14 | this.title = title; 15 | } 16 | 17 | public String getRule() { 18 | return rule; 19 | } 20 | 21 | public void setRule(String rule) { 22 | this.rule = rule; 23 | } 24 | 25 | public String getExamples() { 26 | return examples; 27 | } 28 | 29 | public void setExamples(String examples) { 30 | this.examples = examples; 31 | } 32 | 33 | public boolean isSelfBuild() { 34 | return selfBuild; 35 | } 36 | 37 | public void setSelfBuild(boolean selfBuild) { 38 | this.selfBuild = selfBuild; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object obj) { 43 | if (obj instanceof RuleModel) { 44 | return isEq(((RuleModel) obj).getTitle(), this.title) && isEq(((RuleModel) obj).rule, this.rule) && isEq(((RuleModel) obj).examples, this.examples); 45 | } 46 | return false; 47 | } 48 | 49 | private boolean isEq(Object value1, Object value2) { 50 | if (value1 != null) { 51 | return value1.equals(value2); 52 | } else if (value2 != null) { 53 | return false; 54 | } else { 55 | return true; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /.github/workflows/gradle-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created 6 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle 7 | 8 | name: Gradle Package 9 | 10 | on: 11 | release: 12 | types: [created] 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: read 20 | packages: write 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up JDK 11 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: '11' 28 | distribution: 'temurin' 29 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 30 | settings-path: ${{ github.workspace }} # location for the settings.xml file 31 | 32 | - name: Build with Gradle 33 | uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 34 | with: 35 | arguments: build 36 | 37 | # The USERNAME and TOKEN need to correspond to the credentials environment variables used in 38 | # the publishing section of your build.gradle 39 | - name: Publish to GitHub Packages 40 | uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 41 | with: 42 | arguments: publish 43 | env: 44 | USERNAME: ${{ github.actor }} 45 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | -------------------------------------------------------------------------------- /src/main/resources/icons/regex_dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/RulePersistentConfig.java: -------------------------------------------------------------------------------- 1 | package cn.olange.setting; 2 | 3 | import cn.olange.model.Config; 4 | import cn.olange.model.RuleModel; 5 | import cn.olange.service.RuleDataService; 6 | import com.intellij.openapi.components.PersistentStateComponent; 7 | import com.intellij.openapi.components.ServiceManager; 8 | import com.intellij.openapi.components.State; 9 | import com.intellij.openapi.components.Storage; 10 | import com.intellij.openapi.util.io.FileUtil; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.io.InputStream; 15 | import java.io.InputStreamReader; 16 | import java.util.List; 17 | 18 | @State(name = "RulePersistentConfig", storages = {@Storage(value = "any-rule-config.xml")}) 19 | public class RulePersistentConfig implements PersistentStateComponent { 20 | 21 | private Config config = new Config(); 22 | @Nullable 23 | @Override 24 | public Config getState() { 25 | if (config.getRegExpList() == null || config.getRegExpList().size() == 0) { 26 | try { 27 | InputStream resourceAsStream = RuleDataService.class.getResourceAsStream("/data/rule.js"); 28 | InputStreamReader inputStreamReader = new InputStreamReader(resourceAsStream, "utf-8"); 29 | String content = FileUtil.loadTextAndClose(inputStreamReader); 30 | List ruleModels = RuleDataService.getRegArray(content); 31 | config.setRegExpList(ruleModels); 32 | } catch (Exception e) { 33 | } 34 | } 35 | return this.config; 36 | } 37 | 38 | @Override 39 | public void loadState(@NotNull Config config) { 40 | this.config = config; 41 | } 42 | 43 | @Nullable 44 | public static RulePersistentConfig getInstance() { 45 | return ServiceManager.getService(RulePersistentConfig.class); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/RuleFormDialog.java: -------------------------------------------------------------------------------- 1 | package cn.olange.setting; 2 | 3 | import cn.olange.model.RuleModel; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.ui.DialogWrapper; 6 | import com.intellij.openapi.util.text.StringUtil; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import javax.swing.*; 10 | 11 | public class RuleFormDialog extends DialogWrapper { 12 | private JPanel main; 13 | private JTextField nameField; 14 | private JTextField ruleField; 15 | private JTextArea exampleField; 16 | private SettingUI settingUI; 17 | private RuleModel ruleModel; 18 | 19 | public RuleFormDialog(@Nullable Project project, SettingUI settingUI, RuleModel ruleModel) { 20 | super(project, true); 21 | this.settingUI = settingUI; 22 | if (ruleModel != null) { 23 | this.nameField.setText(ruleModel.getTitle()); 24 | this.ruleField.setText(ruleModel.getRule()); 25 | this.exampleField.setText(ruleModel.getExamples()); 26 | this.setTitle("修改规则"); 27 | this.ruleModel = ruleModel; 28 | } else { 29 | this.setTitle("新增规则"); 30 | } 31 | init(); 32 | } 33 | 34 | @Override 35 | protected @Nullable JComponent createCenterPanel() { 36 | return main; 37 | } 38 | 39 | 40 | protected void doOKAction() { 41 | String rule = ruleField.getText(); 42 | String title = nameField.getText(); 43 | if (StringUtil.isEmpty(rule)) { 44 | this.setErrorText("规则不能为空"); 45 | return; 46 | } 47 | if (StringUtil.isEmpty(title)) { 48 | this.setErrorText("规则名称不能为空"); 49 | return; 50 | } 51 | if (ruleModel != null) { 52 | ruleModel.setRule(rule); 53 | ruleModel.setTitle(title); 54 | ruleModel.setExamples(exampleField.getText()); 55 | } else { 56 | ruleModel = new RuleModel(); 57 | ruleModel.setRule(rule); 58 | ruleModel.setTitle(title); 59 | ruleModel.setExamples(exampleField.getText()); 60 | ruleModel.setSelfBuild(true); 61 | settingUI.addRule(ruleModel); 62 | } 63 | settingUI.updateTableUI(); 64 | super.doOKAction(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/service/RuleDataService.java: -------------------------------------------------------------------------------- 1 | package cn.olange.service; 2 | 3 | import cn.olange.model.AsyncResult; 4 | import cn.olange.model.Config; 5 | import cn.olange.model.Handler; 6 | import cn.olange.model.RuleModel; 7 | import cn.olange.setting.RulePersistentConfig; 8 | import com.google.gson.JsonArray; 9 | import com.google.gson.JsonObject; 10 | import org.apache.commons.lang.StringUtils; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | public class RuleDataService { 18 | public static void filterRule(String keyword, Handler>> handler) { 19 | Config config = RulePersistentConfig.getInstance().getState(); 20 | List regExpList = config.getRegExpList(); 21 | if (StringUtils.isEmpty(keyword)) { 22 | handler.handle(new AsyncResult(true, regExpList)); 23 | return; 24 | } 25 | List matchResult = new ArrayList<>(); 26 | Pattern pattern = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE); 27 | for (int i = 0; i < regExpList.size(); i++) { 28 | RuleModel ruleModel = regExpList.get(i); 29 | String title = ruleModel.getTitle(); 30 | if (pattern.matcher(title).find()) { 31 | matchResult.add(ruleModel); 32 | } 33 | } 34 | handler.handle(new AsyncResult<>(true, matchResult)); 35 | } 36 | 37 | public static List getRegArray(String content) { 38 | List ruleModels = new ArrayList<>(); 39 | if (StringUtils.isNotEmpty(content)) { 40 | Pattern compile = Pattern.compile("\\{[\r|\n]+([\\d\\D]*?)[\r|\n]+ *?}"); 41 | Matcher matcher = compile.matcher(content); 42 | Pattern contentPattern = Pattern.compile("title: \'([\\d\\D]*?)\',[\\d\\D]*?rule: ([\\d\\D]*?),[\r|\n]+[\\d\\D]*?examples: \\[([\\d\\D]*?)]"); 43 | while (matcher.find()) { 44 | String group = matcher.group(1); 45 | Matcher matcher1 = contentPattern.matcher(group); 46 | if (matcher1.find()) { 47 | String title = matcher1.group(1); 48 | String rule = matcher1.group(2); 49 | String examples = matcher1.group(3); 50 | RuleModel ruleObj = new RuleModel(); 51 | ruleObj.setTitle(title); 52 | ruleObj.setRule(rule); 53 | ruleObj.setExamples(examples); 54 | ruleObj.setSelfBuild(false); 55 | ruleModels.add(ruleObj); 56 | } 57 | } 58 | } 59 | return ruleModels; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | 8 | name: Java CI with Gradle 9 | 10 | on: 11 | push: 12 | branches: [ "master" ] 13 | pull_request: 14 | branches: [ "master" ] 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: read 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up JDK 17 26 | uses: actions/setup-java@v4 27 | with: 28 | java-version: '17' 29 | distribution: 'temurin' 30 | 31 | # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. 32 | # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md 33 | - name: Setup Gradle 34 | uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 35 | - name: Make gradlew executable 36 | run: chmod +x ./gradlew 37 | - name: Build with Gradle Wrapper 38 | run: ./gradlew build 39 | 40 | # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). 41 | # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. 42 | # 43 | # - name: Setup Gradle 44 | # uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 45 | # with: 46 | # gradle-version: '8.9' 47 | # 48 | # - name: Build with Gradle 8.9 49 | # run: gradle build 50 | 51 | dependency-submission: 52 | 53 | runs-on: ubuntu-latest 54 | permissions: 55 | contents: write 56 | 57 | steps: 58 | - uses: actions/checkout@v4 59 | - name: Set up JDK 17 60 | uses: actions/setup-java@v4 61 | with: 62 | java-version: '17' 63 | distribution: 'temurin' 64 | 65 | # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. 66 | # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md 67 | - name: Generate and submit dependency graph 68 | uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 69 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/RuleFormDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | cn.olange.rule 3 | any-rule 4 | 1.0.5.2 5 | any-rule 6 | 7 | 9 |

General RegEx Tool

10 |
    11 |
  • 1.Quick to search
  • 12 |
  • 2.Easy to use
  • 13 |
  • 3.This tool is free for personal and commercial usage
  • 14 |
15 |

常用正则大全

16 |
    17 |
  • 1.快速检索
  • 18 |
  • 2.方便使用
  • 19 |
  • 3.免费使用
  • 20 |
21 | 22 | ]]>
23 | 25 |

Version 1.0.5.2

26 |
    27 |
  • [fix] 修复版本打包问题
  • 28 |
29 |

Version 1.0.5.1

30 |
    31 |
  • [fix] 修复冗余字符导致的正则校验失败的bug
  • 32 |
33 |

Version 1.0.5

34 |
    35 |
  • [update] 优化正则配置页,简化自定义正则
  • 36 |
  • [update] 优化正则校验页面
  • 37 |
38 |

Version 1.0.4

39 |
    40 |
  • [add] 添加配置,允许自定义正则库
  • 41 |
  • [add] 支持远程更新正则库
  • 42 |
43 |

Version 1.0.3

44 |
    45 |
  • [add] 添加正则匹配实时校验
  • 46 |
47 |

Version 1.0.2.1

48 |
    49 |
  • [fix] 高版本StringEscapUtil找不到的bug
  • 50 |
51 |

Version 1.0.2

52 |
    53 |
  • [fix] 修复数组越界的bug
  • 54 |
  • [fix] 修复低版本中文乱码的bug
  • 55 |
  • [update] 正则数据同步更新
  • 56 |
  • [update] 默认选中搜索输入框,优化使用体验
  • 57 |
  • [add] 添加空行匹配正则
  • 58 |
  • [add] 添加自动去除首尾斜杆选项
  • 59 |
60 |

Version 1.0.1

61 |
    62 |
  • [add] 支持快捷键和鼠标右键两种调取工具方式
  • 63 |
  • [add] 支持模糊查询
  • 64 |
  • [add] 可以选中合适的正则直接添加到文本编辑器中
  • 65 |
  • [add] 支持正则预览
  • 66 |
67 | 68 | ]]> 69 |
70 | 71 | 72 | 73 | 74 | 76 | 78 | com.intellij.modules.lang 79 | 80 | 81 | 83 | 85 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/utils/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package cn.olange.utils; 2 | 3 | import org.apache.http.client.ResponseHandler; 4 | import org.apache.http.client.config.RequestConfig; 5 | import org.apache.http.client.methods.HttpGet; 6 | import org.apache.http.client.methods.HttpPost; 7 | import org.apache.http.conn.ssl.NoopHostnameVerifier; 8 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 9 | import org.apache.http.entity.StringEntity; 10 | import org.apache.http.impl.client.BasicResponseHandler; 11 | import org.apache.http.impl.client.CloseableHttpClient; 12 | import org.apache.http.impl.client.HttpClients; 13 | import org.apache.http.ssl.SSLContextBuilder; 14 | import org.apache.http.ssl.TrustStrategy; 15 | 16 | import javax.net.ssl.HostnameVerifier; 17 | import javax.net.ssl.SSLContext; 18 | import java.io.IOException; 19 | import java.security.KeyManagementException; 20 | import java.security.KeyStoreException; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.security.cert.CertificateException; 23 | import java.security.cert.X509Certificate; 24 | 25 | public class HttpUtil { 26 | public static String postJson(String url, String data) throws IOException { 27 | CloseableHttpClient httpClient = createSSLClientDefault(); 28 | ResponseHandler responseHandler = new BasicResponseHandler(); 29 | HttpPost httpPost = new HttpPost(url); 30 | RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(8000).setConnectTimeout(8000).build(); 31 | httpPost.setConfig(requestConfig); 32 | StringEntity requestEntity = new StringEntity(data,"utf-8"); 33 | httpPost.addHeader("Content-Type", "application/json;charset=utf-8"); 34 | httpPost.addHeader("X-Agent", "Juejin/Web"); 35 | httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36"); 36 | httpPost.setEntity(requestEntity); 37 | return httpClient.execute(httpPost, responseHandler); 38 | } 39 | 40 | public static String getJson(String url) throws IOException { 41 | CloseableHttpClient httpClient = createSSLClientDefault(); 42 | ResponseHandler responseHandler = new BasicResponseHandler(); 43 | HttpGet httpGet = new HttpGet(url); 44 | RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build(); 45 | httpGet.setConfig(requestConfig); 46 | httpGet.addHeader("Content-Type", "application/json;charset=utf-8"); 47 | httpGet.addHeader("X-Agent", "Juejin/Web"); 48 | httpGet.addHeader("X-Juejin-Src","web"); 49 | httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36"); 50 | return httpClient.execute(httpGet, responseHandler); 51 | } 52 | 53 | public static CloseableHttpClient createSSLClientDefault() { 54 | try { 55 | //使用 loadTrustMaterial() 方法实现一个信任策略,信任所有证书 56 | SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { 57 | // 信任所有 58 | public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { 59 | return true; 60 | } 61 | }).build(); 62 | //NoopHostnameVerifier类: 作为主机名验证工具,实质上关闭了主机名验证,它接受任何 63 | //有效的SSL会话并匹配到目标主机。 64 | HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE; 65 | SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); 66 | return HttpClients.custom().setSSLSocketFactory(sslsf).build(); 67 | } catch (KeyManagementException e) { 68 | e.printStackTrace(); 69 | } catch (NoSuchAlgorithmException e) { 70 | e.printStackTrace(); 71 | } catch (KeyStoreException e) { 72 | e.printStackTrace(); 73 | } 74 | return HttpClients.createDefault(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/SettingUI.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/ui/RulePreviewPanel.java: -------------------------------------------------------------------------------- 1 | package cn.olange.ui; 2 | 3 | import cn.olange.model.RuleModel; 4 | import cn.olange.utils.RuleUtil; 5 | import com.intellij.icons.AllIcons; 6 | import com.intellij.openapi.Disposable; 7 | import com.intellij.openapi.application.ApplicationManager; 8 | import com.intellij.openapi.application.ModalityState; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.ui.OnePixelDivider; 11 | import com.intellij.openapi.util.SystemInfo; 12 | import com.intellij.openapi.util.text.StringUtil; 13 | import com.intellij.ui.DocumentAdapter; 14 | import com.intellij.ui.IdeBorderFactory; 15 | import com.intellij.ui.JBSplitter; 16 | import com.intellij.ui.components.JBPanelWithEmptyText; 17 | import com.intellij.ui.components.JBScrollPane; 18 | import com.intellij.ui.scale.JBUIScale; 19 | import com.intellij.util.Alarm; 20 | import com.intellij.util.ui.JBInsets; 21 | import com.intellij.util.ui.JBUI; 22 | import com.intellij.util.ui.UIUtil; 23 | import org.apache.commons.lang.StringUtils; 24 | import org.jdesktop.swingx.JXTextArea; 25 | 26 | import javax.swing.*; 27 | import javax.swing.border.Border; 28 | import javax.swing.event.DocumentEvent; 29 | import javax.swing.plaf.TextUI; 30 | import javax.swing.text.AttributeSet; 31 | import javax.swing.text.BadLocationException; 32 | import javax.swing.text.PlainDocument; 33 | import java.awt.*; 34 | import java.util.regex.Matcher; 35 | import java.util.regex.Pattern; 36 | 37 | public class RulePreviewPanel extends JBPanelWithEmptyText implements Disposable { 38 | private JXTextArea textArea; 39 | private Project project; 40 | private JLabel statusText; 41 | private JTextArea checkTextArea; 42 | private Alarm checkAlarm; 43 | private JTextArea exampleText; 44 | 45 | public static final KeyStroke NEW_LINE_KEYSTROKE = KeyStroke.getKeyStroke(10, (SystemInfo.isMac ? 256 : 128) | 64); 46 | public static final String SPLITTER_SERVICE_KEY = "any.rule.preview.splitter"; 47 | 48 | 49 | 50 | public RulePreviewPanel(Project project) { 51 | super(); 52 | this.project = project; 53 | this.checkAlarm = new Alarm(); 54 | this.setLayout(new BorderLayout()); 55 | this.setBorder(JBUI.Borders.empty()); 56 | textArea = new JXTextArea(); 57 | this.textArea.setEditable(false); 58 | this.exampleText = new JXTextArea(); 59 | this.exampleText.setEditable(false); 60 | this.exampleText.setLineWrap(true); 61 | this.exampleText.setWrapStyleWord(true); 62 | this.checkTextArea = new JTextArea(); 63 | this.checkTextArea.setLineWrap(true); 64 | this.checkTextArea.setWrapStyleWord(true); 65 | this.checkTextArea.setDocument(new PlainDocument() { 66 | public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { 67 | if (this.getProperty("filterNewlines") == Boolean.TRUE && str.indexOf(10) >= 0) { 68 | str = StringUtil.replace(str, "\n", " "); 69 | } 70 | if (!StringUtil.isEmpty(str)) { 71 | super.insertString(offs, str, a); 72 | } 73 | 74 | } 75 | }); 76 | this.checkTextArea.getDocument().putProperty("trimTextOnPaste", Boolean.TRUE); 77 | this.checkTextArea.getDocument().addDocumentListener(new DocumentAdapter() { 78 | protected void textChanged(DocumentEvent e) { 79 | RulePreviewPanel.this.checkTextChange(); 80 | } 81 | }); 82 | this.checkTextArea.setOpaque(false); 83 | 84 | JBScrollPane jbScrollPane = new JBScrollPane(this.checkTextArea, 20, 30) { 85 | public Dimension getPreferredSize() { 86 | Dimension d = super.getPreferredSize(); 87 | TextUI ui = RulePreviewPanel.this.checkTextArea.getUI(); 88 | if (ui != null) { 89 | d.height = Math.min(d.height, ui.getPreferredSize(RulePreviewPanel.this.checkTextArea).height); 90 | } 91 | 92 | return d; 93 | } 94 | }; 95 | this.checkTextArea.setBorder(new Border() { 96 | public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { 97 | } 98 | 99 | public Insets getBorderInsets(Component c) { 100 | if (SystemInfo.isMac && !UIUtil.isUnderDarcula()) { 101 | return new JBInsets(3, 0, 3, 0); 102 | } else { 103 | int bottom = StringUtil.getLineBreakCount(RulePreviewPanel.this.checkTextArea.getText()) > 0 ? 2 : (UIUtil.isUnderDarcula() ? 2 : 1); 104 | int top = RulePreviewPanel.this.checkTextArea.getFontMetrics(RulePreviewPanel.this.checkTextArea.getFont()).getHeight() <= 16 ? 2 : 1; 105 | if (JBUIScale.isUsrHiDPI()) { 106 | bottom = 2; 107 | top = 2; 108 | } 109 | 110 | return new JBInsets(top, 0, bottom, 0); 111 | } 112 | } 113 | 114 | public boolean isBorderOpaque() { 115 | return false; 116 | } 117 | }); 118 | JBScrollPane scrollPane = new JBScrollPane(this.textArea); 119 | scrollPane.setBorder(JBUI.Borders.empty()); 120 | this.add(scrollPane,BorderLayout.NORTH); 121 | jbScrollPane.setBorder(IdeBorderFactory.createTitledBorder("验证", false, new JBInsets(8, 0, 0, 0)).setShowLine(true)); 122 | JPanel bootomPanel = new JPanel(); 123 | bootomPanel.setLayout(new BorderLayout()); 124 | statusText = new JLabel(); 125 | bootomPanel.add(statusText, BorderLayout.WEST); 126 | this.add(bootomPanel, BorderLayout.SOUTH); 127 | 128 | JBSplitter splitter = new JBSplitter(false,0.55F); 129 | splitter.setSplitterProportionKey(SPLITTER_SERVICE_KEY); 130 | splitter.setDividerWidth(1); 131 | splitter.getDivider().setBackground(OnePixelDivider.BACKGROUND); 132 | splitter.setFirstComponent(jbScrollPane); 133 | JBScrollPane exampleScroll = new JBScrollPane(this.exampleText, 20, 30); 134 | exampleScroll.setBorder(JBUI.Borders.empty()); 135 | exampleScroll.setBorder(IdeBorderFactory.createTitledBorder("示例", true, new JBInsets(8, 10, 0, 0)).setShowLine(true)); 136 | splitter.setSecondComponent(exampleScroll); 137 | this.add(splitter, BorderLayout.CENTER); 138 | 139 | 140 | } 141 | 142 | private void checkTextChange(){ 143 | if (this.checkAlarm != null && !this.checkAlarm.isDisposed()) { 144 | this.checkAlarm.cancelAllRequests(); 145 | this.checkAlarm.addRequest(() -> { 146 | String text = checkTextArea.getText(); 147 | if (StringUtils.isNotEmpty(text)) { 148 | String rule = RuleUtil.convertRule(textArea.getText()); 149 | Pattern pattern = Pattern.compile(rule); 150 | Matcher matcher = pattern.matcher(text); 151 | if (matcher.find()) { 152 | statusText.setIcon(AllIcons.RunConfigurations.TestPassed); 153 | } else { 154 | statusText.setIcon(AllIcons.RunConfigurations.TestError); 155 | } 156 | } else { 157 | statusText.setIcon(null); 158 | } 159 | }, 200); 160 | } 161 | } 162 | 163 | public void updateLayout(RuleModel ruleModel) { 164 | ApplicationManager.getApplication().invokeLater(()->{RulePreviewPanel.this.updateLayoutLater(ruleModel);}, ModalityState.any()); 165 | } 166 | 167 | public void updateLayoutLater(RuleModel row) { 168 | String title = row.getTitle(); 169 | String rule = row.getRule(); 170 | String examples = row.getExamples(); 171 | this.setBorder(IdeBorderFactory.createTitledBorder(title, false, new JBInsets(8, 0, 0, 0)).setShowLine(false)); 172 | this.setToolTipText(title); 173 | this.textArea.setText(rule); 174 | this.checkTextArea.setText(""); 175 | this.exampleText.setText(examples); 176 | } 177 | 178 | @Override 179 | public void dispose() { 180 | if (this.checkAlarm != null && !this.checkAlarm.isDisposed()) { 181 | this.checkAlarm.cancelAllRequests(); 182 | this.checkAlarm.dispose(); 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/setting/SettingUI.java: -------------------------------------------------------------------------------- 1 | package cn.olange.setting; 2 | 3 | import cn.olange.model.Config; 4 | import cn.olange.model.RegExTableModel; 5 | import cn.olange.model.RuleModel; 6 | import cn.olange.service.RuleDataService; 7 | import cn.olange.setting.action.DeleteRuleAction; 8 | import cn.olange.setting.action.NewRuleAction; 9 | import cn.olange.utils.HttpUtil; 10 | import com.intellij.ide.BrowserUtil; 11 | import com.intellij.openapi.actionSystem.ActionGroup; 12 | import com.intellij.openapi.actionSystem.ActionManager; 13 | import com.intellij.openapi.actionSystem.DefaultActionGroup; 14 | import com.intellij.openapi.application.ApplicationManager; 15 | import com.intellij.openapi.command.WriteCommandAction; 16 | import com.intellij.openapi.ui.popup.Balloon; 17 | import com.intellij.openapi.ui.popup.JBPopupFactory; 18 | import com.intellij.openapi.util.Comparing; 19 | import com.intellij.ui.DoubleClickListener; 20 | import com.intellij.ui.awt.RelativePoint; 21 | import com.intellij.util.ui.UIUtil; 22 | import javax.swing.*; 23 | import java.awt.*; 24 | import java.awt.event.ActionEvent; 25 | import java.awt.event.MouseAdapter; 26 | import java.awt.event.MouseEvent; 27 | import java.io.IOException; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.stream.Collectors; 32 | 33 | public class SettingUI { 34 | private JTextField remoteUrl; 35 | private JButton dataUpdate; 36 | private JPanel mainPanel; 37 | private JPanel contentPanel; 38 | private JLabel helpbtn; 39 | private JTable reTable; 40 | private JPanel toolbar; 41 | private List tmpRuleModels; 42 | 43 | 44 | public SettingUI() { 45 | JComponent actionToolbar = ActionManager.getInstance().createActionToolbar("操作", createActionGroup(), true).getComponent(); 46 | this.toolbar.setLayout(new BorderLayout()); 47 | this.toolbar.add(actionToolbar, BorderLayout.WEST); 48 | this.helpbtn.addMouseListener(new MouseAdapter() { 49 | @Override 50 | public void mouseClicked(MouseEvent e) { 51 | BrowserUtil.browse("https://github.com/Linindoo/idea-rule"); 52 | } 53 | }); 54 | reTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 55 | (new DoubleClickListener() { 56 | protected boolean onDoubleClick(MouseEvent event) { 57 | if (event.getSource() != SettingUI.this.reTable) { 58 | return false; 59 | } else { 60 | RuleModel selectRow = SettingUI.this.getSelectRow(); 61 | if (selectRow != null) { 62 | RuleFormDialog ruleFormDialog = new RuleFormDialog(null, SettingUI.this, selectRow); 63 | ruleFormDialog.show(); 64 | } 65 | return true; 66 | } 67 | } 68 | }).installOn(this.reTable); 69 | this.reTable.addMouseListener(new MouseAdapter() { 70 | public void mousePressed(MouseEvent e) { 71 | SettingUI.this.reTable.transferFocus(); 72 | } 73 | }); 74 | this.dataUpdate.addActionListener(new AbstractAction() { 75 | @Override 76 | public void actionPerformed(ActionEvent e) { 77 | dataUpdate.setEnabled(false); 78 | ApplicationManager.getApplication().executeOnPooledThread(()->{ 79 | try { 80 | String ret = HttpUtil.getJson(remoteUrl.getText()); 81 | UIUtil.invokeLaterIfNeeded(() -> { 82 | WriteCommandAction.runWriteCommandAction(null, () -> { 83 | saveRemoteData(ret); 84 | }); 85 | }); 86 | UIUtil.invokeLaterIfNeeded(() -> { 87 | dataUpdate.setEnabled(true); 88 | JBPopupFactory.getInstance().createBalloonBuilder(new JLabel("数据已更新")) 89 | .setFillColor(contentPanel.getBackground()) 90 | .setAnimationCycle(0) 91 | .setFadeoutTime(0) 92 | .setRequestFocus(true) 93 | .createBalloon() 94 | .show(new RelativePoint(contentPanel, new Point(10, 10)), Balloon.Position.above); 95 | }); 96 | } catch (IOException ioException) { 97 | UIUtil.invokeLaterIfNeeded(() -> { 98 | dataUpdate.setEnabled(true); 99 | JBPopupFactory.getInstance().createBalloonBuilder(new JLabel("获取失败,请检查地址是否正确或者网络是否可用")) 100 | .setFillColor(contentPanel.getBackground()) 101 | .setAnimationCycle(0) 102 | .setFadeoutTime(0) 103 | .setRequestFocus(true) 104 | .createBalloon() 105 | .show(new RelativePoint(remoteUrl, new Point(10, 10)), Balloon.Position.below); 106 | }); 107 | } 108 | }); 109 | } 110 | }); 111 | } 112 | 113 | private boolean ruleIsChange(List oldRules) { 114 | if (oldRules == null && this.tmpRuleModels != null) { 115 | return true; 116 | } 117 | if (oldRules != null && this.tmpRuleModels == null) { 118 | return true; 119 | } 120 | if (oldRules == null) { 121 | return false; 122 | } 123 | if (oldRules.size() != tmpRuleModels.size()) { 124 | return true; 125 | } 126 | for (int i = 0; i < oldRules.size(); i++) { 127 | RuleModel oldRule = oldRules.get(i); 128 | if (!oldRule.equals(tmpRuleModels.get(i))) { 129 | return true; 130 | } 131 | } 132 | return false; 133 | } 134 | 135 | public RuleModel getSelectRow() { 136 | int selectedRow = this.reTable.getSelectedRow(); 137 | if (selectedRow < 0 || selectedRow > this.tmpRuleModels.size()) { 138 | return null; 139 | } 140 | return this.tmpRuleModels.get(selectedRow); 141 | } 142 | 143 | private ActionGroup createActionGroup() { 144 | DefaultActionGroup actionGroup = new DefaultActionGroup(); 145 | actionGroup.add(new NewRuleAction(this)); 146 | actionGroup.add(new DeleteRuleAction(this)); 147 | return actionGroup; 148 | } 149 | 150 | public JComponent getComponent() { 151 | return mainPanel; 152 | } 153 | 154 | public boolean isModified() { 155 | Config config = RulePersistentConfig.getInstance().getState(); 156 | return !Comparing.strEqual(config.getUpdateUrl(), remoteUrl.getText()) || ruleIsChange(config.getRegExpList()); 157 | } 158 | 159 | public void apply() { 160 | Config config = RulePersistentConfig.getInstance().getState(); 161 | config.setUpdateUrl(this.remoteUrl.getText()); 162 | List regExpList = new ArrayList<>(); 163 | for (RuleModel ruleModel : tmpRuleModels) { 164 | RuleModel tmpRule = new RuleModel(); 165 | tmpRule.setRule(ruleModel.getRule()); 166 | tmpRule.setTitle(ruleModel.getTitle()); 167 | tmpRule.setExamples(ruleModel.getExamples()); 168 | tmpRule.setSelfBuild(ruleModel.isSelfBuild()); 169 | regExpList.add(tmpRule); 170 | } 171 | config.setRegExpList(regExpList); 172 | } 173 | 174 | private void saveRemoteData(String text) { 175 | List ruleModels = RuleDataService.getRegArray(text); 176 | if (ruleModels.size() == 0) { 177 | JBPopupFactory.getInstance().createBalloonBuilder(new JLabel("未匹配到正则")) 178 | .setFillColor(contentPanel.getBackground()) 179 | .setAnimationCycle(0) 180 | .setFadeoutTime(0) 181 | .setRequestFocus(true) 182 | .createBalloon() 183 | .show(new RelativePoint(contentPanel, new Point(0, 0)), Balloon.Position.below); 184 | return; 185 | } 186 | Map ruleMap = ruleModels.stream().collect(Collectors.toMap(RuleModel::getTitle, x -> x)); 187 | for (RuleModel tmpRuleModel : this.tmpRuleModels) { 188 | if (!tmpRuleModel.isSelfBuild()) { 189 | RuleModel ruleModel = ruleMap.get(tmpRuleModel.getTitle()); 190 | if (ruleModel != null) { 191 | tmpRuleModel.setRule(ruleModel.getRule()); 192 | tmpRuleModel.setExamples(ruleModel.getExamples()); 193 | } 194 | } 195 | } 196 | Map tmpMapx = tmpRuleModels.stream().filter(x -> !x.isSelfBuild()).collect(Collectors.toMap(RuleModel::getTitle, x -> x)); 197 | for (RuleModel ruleModel : ruleModels) { 198 | RuleModel newModel = tmpMapx.get(ruleModel.getTitle()); 199 | if (newModel == null) { 200 | tmpRuleModels.add(ruleModel); 201 | } 202 | } 203 | } 204 | 205 | public void reset() { 206 | Config config = RulePersistentConfig.getInstance().getState(); 207 | this.remoteUrl.setText(config.getUpdateUrl()); 208 | List regExpList = config.getRegExpList(); 209 | this.tmpRuleModels = new ArrayList<>(); 210 | for (RuleModel ruleModel : regExpList) { 211 | RuleModel tmpRule = new RuleModel(); 212 | tmpRule.setRule(ruleModel.getRule()); 213 | tmpRule.setTitle(ruleModel.getTitle()); 214 | tmpRule.setExamples(ruleModel.getExamples()); 215 | tmpRule.setSelfBuild(ruleModel.isSelfBuild()); 216 | tmpRuleModels.add(tmpRule); 217 | } 218 | reTable.setModel(new RegExTableModel(this.tmpRuleModels)); 219 | } 220 | 221 | public void addRule(RuleModel ruleModel) { 222 | tmpRuleModels.add(ruleModel); 223 | } 224 | 225 | public void updateTableUI() { 226 | this.reTable.updateUI(); 227 | } 228 | 229 | public void deleteSelectRow() { 230 | int selectedRow = this.reTable.getSelectedRow(); 231 | if (selectedRow < 0 || selectedRow > this.tmpRuleModels.size()) { 232 | return; 233 | } 234 | this.tmpRuleModels.remove(selectedRow); 235 | this.updateTableUI(); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/main/resources/data/rule.js: -------------------------------------------------------------------------------- 1 | module.exports = [{ 2 | title: '火车车次', 3 | rule: /^[GCDZTSPKXLY1-9]\d{1,4}$/, 4 | examples: ['G1868', 'D102', 'D9', 'Z5', 'Z24', 'Z17'] 5 | }, 6 | { 7 | title: '手机机身码(IMEI)', 8 | rule: /^\d{15,17}$/, 9 | examples: ['123456789012345', '1234567890123456', '12345678901234567'] 10 | }, 11 | { 12 | title: '必须带端口号的网址(或ip)', 13 | rule: /^((ht|f)tps?:\/\/)?[\w-]+(\.[\w-]+)+:\d{1,5}\/?$/, 14 | examples: ['https://www.qq.com:8080', '127.0.0.1:5050', 'baidu.com:8001', 'http://192.168.1.1:9090'], 15 | counterExamples: ['192.168.1.1', 'https://www.jd.com'] 16 | }, 17 | { 18 | title: '网址(url,支持端口和"?+参数"和"#+参数)', 19 | rule: /^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?$/, 20 | examples: ['www.qq.com', 'https://baidu.com', '360.com:8080/vue/#/a=1&b=2'], 21 | counterExamples: ['....'] 22 | }, 23 | { 24 | title: '统一社会信用代码', 25 | rule: /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/, 26 | examples: ['91230184MA1BUFLT44', '92371000MA3MXH0E3W'], 27 | }, 28 | { 29 | title: '统一社会信用代码(宽松匹配)(15位/18位/20位数字/字母)', 30 | rule: /^(([0-9A-Za-z]{15})|([0-9A-Za-z]{18})|([0-9A-Za-z]{20}))$/, 31 | examples: ['91110108772551611J', '911101085923662400'] 32 | }, 33 | { 34 | title: '迅雷链接', 35 | rule: /^thunderx?:\/\/[a-zA-Z\d]+=$/, 36 | examples: ['thunder://QUEsICdtYWduZXQ6P3h0PXVybjpidGloOjBCQTE0RTUxRkUwNjU1RjE0Qzc4NjE4RjY4NDY0QjZFNTEyNjcyOUMnWlo='], 37 | }, 38 | 39 | { 40 | title: 'ed2k链接(宽松匹配)', 41 | rule: /^ed2k:\/\/\|file\|.+\|\/$/, 42 | examples: ['ed2k://|file|%E5%AF%84%E7%94%9F%E8%99%AB.PARASITE.2019.HD-1080p.X264.AAC-UUMp4(ED2000.COM).mp4|2501554832|C0B93E0879C6071CBED732C20CE577A3|h=5HTKZPQFYRKORN52I3M7GQ4QQCIHFIBV|/'], 43 | }, 44 | 45 | { 46 | title: '磁力链接(宽松匹配)', 47 | rule: /^magnet:\?xt=urn:btih:[0-9a-fA-F]{40,}.*$/, 48 | examples: ['magnet:?xt=urn:btih:40A89A6F4FB1498A98087109D012A9A851FBE0FC'], 49 | }, 50 | { 51 | title: '子网掩码', 52 | rule: /^(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/, 53 | examples: ['255.255.255.0', '255.224.0.0'] 54 | }, 55 | { 56 | title: 'linux"隐藏文件"路径', 57 | rule: /^\/(?:[^/]+\/)*\.[^/]*/, 58 | examples: ['/usr/ad/.dd', '/root/.gitignore', '/.gitignore'] 59 | }, 60 | { 61 | title: 'linux文件夹路径', 62 | rule: /^\/(?:[^/]+\/)*$/, 63 | examples: ['/usr/ad/dd/', '/', '/root/'] 64 | }, 65 | { 66 | title: 'linux文件路径', 67 | rule: /^\/(?:[^/]+\/)*[^/]+$/, 68 | examples: ['/root/b.ts', '/root/abc'] 69 | }, 70 | { 71 | title: 'window"文件夹"路径', 72 | rule: /^[a-zA-Z]:\\(?:\w+\\?)*$/, 73 | examples: ['C:\\Users\\Administrator\\Desktop', 'e:\\m\\'] 74 | }, 75 | { 76 | title: 'window下"文件"路径', 77 | rule: /^[a-zA-Z]:\\(?:\w+\\)*\w+\.\w+$/, 78 | examples: ['C:\\Users\\Administrator\\Desktop\\qq.link', 'e:\\m\\vscode.exe'] 79 | }, 80 | { 81 | title: '股票代码(A股)', 82 | rule: /^(s[hz]|S[HZ])(000[\d]{3}|002[\d]{3}|300[\d]{3}|600[\d]{3}|60[\d]{4})$/, 83 | examples: ['sz000858', 'SZ002136', 'sz300675', 'SH600600', 'sh601155'] 84 | }, 85 | { 86 | title: '大于等于0, 小于等于150, 支持小数位出现5, 如145.5, 用于判断考卷分数', 87 | rule: /^150$|^(?:\d|[1-9]\d|1[0-4]\d)(?:\.5)?$/, 88 | examples: [150, 100.5] 89 | }, 90 | { 91 | title: 'html注释', 92 | rule: /^$/, 93 | examples: [''] 94 | }, 95 | { 96 | title: 'md5格式(32位)', 97 | rule: /^([a-f\d]{32}|[A-F\d]{32})$/, 98 | examples: ['21fe181c5bfc16306a6828c1f7b762e8'], 99 | }, 100 | { 101 | title: 'GUID/UUID', 102 | rule: /^[a-f\d]{4}(?:[a-f\d]{4}-){4}[a-f\d]{12}$/i, 103 | examples: ['e155518c-ca1b-443c-9be9-fe90fdab7345', '41E3DAF5-6E37-4BCC-9F8E-0D9521E2AA8D', '00000000-0000-0000-0000-000000000000'], 104 | }, 105 | { 106 | title: '版本号(version)格式必须为X.Y.Z', 107 | rule: /^\d+(?:\.\d+){2}$/, 108 | examples: ['16.3.10'] 109 | }, 110 | { 111 | title: '视频(video)链接地址(视频格式可按需增删)', 112 | rule: /^https?:\/\/(.+\/)+.+(\.(swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|mp4))$/i, 113 | examples: ['http://www.abc.com/video/wc.avi'] 114 | }, 115 | { 116 | title: '图片(image)链接地址(图片格式可按需增删)', 117 | rule: /^https?:\/\/(.+\/)+.+(\.(gif|png|jpg|jpeg|webp|svg|psd|bmp|tif))$/i, 118 | examples: ['https://www.abc.com/logo.png'] 119 | }, 120 | { 121 | title: '24小时制时间(HH:mm:ss)', 122 | rule: /^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/, 123 | examples: ['23:34:55'] 124 | }, 125 | { 126 | title: '12小时制时间(hh:mm:ss)', 127 | rule: /^(?:1[0-2]|0?[1-9]):[0-5]\d:[0-5]\d$/, 128 | examples: ['11:34:55'], 129 | counterExamples: ['23:34:55'] 130 | }, 131 | { 132 | title: 'base64格式', 133 | rule: /^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i, 134 | examples: ['data:image/gif;base64,xxxx=='] 135 | }, 136 | { 137 | title: '数字/货币金额(支持负数、千分位分隔符)', 138 | rule: /^-?\d+(,\d{3})*(\.\d{1,2})?$/, 139 | examples: [100, -0.99, 3, 234.32, -1, 900, 235.09, '12,345,678.90'] 140 | }, 141 | { 142 | title: '数字/货币金额 (只支持正数、不支持校验千分位分隔符)', 143 | rule: /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/, 144 | examples: [0.99, 8.99, 666] 145 | }, 146 | { 147 | title: '银行卡号(10到30位, 覆盖对公/私账户, 参考[微信支付](https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=22_1))', 148 | rule: /^[1-9]\d{9,29}$/, 149 | examples: [6234567890, 6222026006705354217] 150 | }, 151 | { 152 | title: '中文姓名', 153 | rule: /^(?:[\u4e00-\u9fa5·]{2,16})$/, 154 | examples: ['葛二蛋', '凯文·杜兰特', '德克·维尔纳·诺维茨基'] 155 | }, 156 | { 157 | title: '英文姓名', 158 | rule: /(^[a-zA-Z][a-zA-Z\s]{0,20}[a-zA-Z]$)/, 159 | examples: ['James', 'Kevin Wayne Durant', 'Dirk Nowitzki'] 160 | }, 161 | { 162 | title: '车牌号(新能源)', 163 | rule: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z](?:((\d{5}[A-HJK])|([A-HJK][A-HJ-NP-Z0-9][0-9]{4}))|[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳])$/, 164 | examples: ['京AD92035', '甘G23459F', '京AA92035'], 165 | }, 166 | { 167 | title: '车牌号(非新能源)', 168 | rule: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]$/, 169 | examples: ['京A00599', '黑D23908'] 170 | }, 171 | { 172 | title: '车牌号(新能源+非新能源)', 173 | rule: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/, 174 | examples: ['京A12345D', '京A00599', '京AD92035', '甘G23459F', '京AA92035'], 175 | counterExamples: ['宁AD1234555555', '浙苏H6F681'] 176 | }, 177 | { 178 | title: '手机号(mobile phone)中国(严谨), 根据工信部2019年最新公布的手机号段', 179 | rule: /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/, 180 | examples: ['008618311006933', '+8617888829981', '19119255642'] 181 | }, 182 | { 183 | title: '手机号(mobile phone)中国(宽松), 只要是13,14,15,16,17,18,19开头即可', 184 | rule: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/, 185 | examples: ['008618311006933', '+8617888829981', '19119255642'] 186 | }, 187 | { 188 | title: '手机号(mobile phone)中国(最宽松), 只要是1开头即可, 如果你的手机号是用来接收短信, 优先建议选择这一条', 189 | rule: /^(?:(?:\+|00)86)?1\d{10}$/, 190 | examples: ['008618311006933', '+8617888829981', '19119255642'] 191 | }, 192 | { 193 | title: 'date(日期)', 194 | rule: /^\d{1,4}(-)(1[0-2]|0?[1-9])\1(0?[1-9]|[1-2]\d|30|31)$/, 195 | examples: ['1990-12-12', '1-1-1','0000-1-1'], 196 | counterExamples: ['2020-00-01'] 197 | }, 198 | { 199 | title: 'email(邮箱)', 200 | rule: /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 201 | examples: ['90203918@qq.com', 'nbilly@126.com'] 202 | }, 203 | 204 | { 205 | title: '座机(tel phone)电话(国内),如: 0341-86091234', 206 | rule: /^(?:(?:\d{3}-)?\d{8}|^(?:\d{4}-)?\d{7,8})(?:-\d+)?$/, 207 | examples: ['0936-4211235', '89076543', '010-12345678-1234'] 208 | }, 209 | 210 | { 211 | title: '身份证号(1代,15位数字)', 212 | rule: /^[1-9]\d{7}(?:0\d|10|11|12)(?:0[1-9]|[1-2][\d]|30|31)\d{3}$/, 213 | examples: ['123456991010193'] 214 | }, 215 | { 216 | title: '身份证号(2代,18位数字),最后一位是校验位,可能为数字或字符X', 217 | rule: /^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/, 218 | examples: ['12345619991205131x'] 219 | }, 220 | { 221 | title: '身份证号, 支持1/2代(15位/18位数字)', 222 | rule: /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/, 223 | examples: ['622223199912051311'] 224 | }, 225 | { 226 | title: '护照(包含香港、澳门)', 227 | rule: /(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/, 228 | examples: ['s28233515', '141234567', '159203084', 'MA1234567', 'K25345719'] 229 | }, 230 | { 231 | title: '帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线组合', 232 | rule: /^[a-zA-Z]\w{4,15}$/, 233 | examples: ['justin', 'justin1989', 'justin_666'] 234 | }, 235 | { 236 | title: '中文/汉字', 237 | // rule: /^[\u4E00-\u9FA5]+$/, 238 | rule: /^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/, 239 | examples: ['正则', '前端'] 240 | }, 241 | { 242 | title: '小数', 243 | rule: /^\d+\.\d+$/, 244 | examples: ['0.0', '0.09'] 245 | }, 246 | { 247 | title: '数字', 248 | rule: /^\d{1,}$/, 249 | examples: [12345678] 250 | }, 251 | { 252 | title: 'html标签(宽松匹配)', 253 | rule: /<(\w+)[^>]*>(.*?<\/\1>)?/, 254 | examples: ['
2333
', '', '
'] 255 | }, 256 | { 257 | title: 'qq号格式正确', 258 | rule: /^[1-9][0-9]{4,10}$/, 259 | examples: [903013545, 9020304] 260 | }, 261 | { 262 | title: '数字和字母组成', 263 | rule: /^[A-Za-z0-9]+$/, 264 | examples: ['james666', 'haha233hi'] 265 | }, 266 | { 267 | title: '英文字母', 268 | rule: /^[a-zA-Z]+$/, 269 | examples: ['Russel'] 270 | }, 271 | { 272 | title: '小写英文字母组成', 273 | rule: /^[a-z]+$/, 274 | examples: ['russel'] 275 | }, 276 | { 277 | title: '大写英文字母', 278 | rule: /^[A-Z]+$/, 279 | examples: ['ABC', 'KD'] 280 | }, 281 | { 282 | title: '密码强度校验,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符', 283 | rule: /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/, 284 | examples: ['Kd@curry666'] 285 | }, 286 | { 287 | title: '用户名校验,4到16位(字母,数字,下划线,减号)', 288 | rule: /^[a-zA-Z0-9_-]{4,16}$/, 289 | examples: ['xiaohua_qq'] 290 | }, 291 | { 292 | title: 'ip-v4[:端口]', 293 | rule: /^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])(?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$/, 294 | examples: ['172.16.0.0', '172.16.0.0:8080', '127.0.0.0', '127.0.0.0:998'] 295 | }, 296 | { 297 | title: 'ip-v6[:端口]', 298 | rule: /^(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))|\[(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))\](?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$/i, 299 | examples: ['2031:0000:130f:0000:0000:09c0:876a:130b', '[2031:0000:130f:0000:0000:09c0:876a:130b]:8080'] 300 | }, 301 | { 302 | title: '16进制颜色', 303 | rule: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/, 304 | examples: ['#f00', '#F90', '#000', '#fe9de8'] 305 | }, 306 | { 307 | title: '微信号(wx),6至20位,以字母开头,字母,数字,减号,下划线', 308 | rule: /^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/, 309 | examples: ['github666', 'kd_-666'] 310 | }, 311 | { 312 | title: '邮政编码(中国)', 313 | rule: /^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/, 314 | examples: ['734500', '100101'] 315 | }, 316 | { 317 | title: '中文和数字', 318 | rule: /^((?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])|(\d))+$/, 319 | examples: ['哈哈哈', '你好6啊'] 320 | }, 321 | { 322 | title: '不能包含字母', 323 | rule: /^[^A-Za-z]*$/, 324 | examples: ['你好6啊', '@¥()!'] 325 | }, 326 | { 327 | title: 'java包名', 328 | rule: /^([a-zA-Z_]\w*)+([.][a-zA-Z_]\w*)+$/, 329 | examples: ['com.bbb.name'] 330 | }, 331 | { 332 | title: 'mac地址', 333 | rule: /^((([a-f0-9]{2}:){5})|(([a-f0-9]{2}-){5}))[a-f0-9]{2}$/i, 334 | examples: ['38:f9:d3:4b:f5:51', '00-0C-29-CA-E4-66'] 335 | }, 336 | { 337 | title: '匹配连续重复的字符', 338 | rule: /(.)\1+/, 339 | examples: ['我我我', '112233', '11234'] 340 | }, 341 | { 342 | title: '数字和英文字母组成,并且同时含有数字和英文字母', 343 | rule: /^(?=.*[a-zA-Z])(?=.*\d).+$/, 344 | examples: ['我a我1我', 'a对1'] 345 | }, 346 | { 347 | title: '香港身份证 ', 348 | rule: /^[a-zA-Z]\d{6}\([\dA]\)$/, 349 | examples: ['K034169(1)'] 350 | }, 351 | { 352 | title: '澳门身份证 ', 353 | rule: /^[1|5|7]\d{6}[(\d)]{3}$/, 354 | examples: ['5686611(1)'] 355 | }, 356 | { 357 | title: '台湾身份证 ', 358 | rule: /^[a-zA-Z][0-9]{9}$/, 359 | examples: ['U193683453'] 360 | }, 361 | ]; 362 | -------------------------------------------------------------------------------- /src/main/java/cn/olange/ui/AnyRulePopupPanel.java: -------------------------------------------------------------------------------- 1 | package cn.olange.ui; 2 | 3 | import cn.olange.model.RuleModel; 4 | import cn.olange.model.UsageTableCellRenderer; 5 | import cn.olange.service.RuleDataService; 6 | import cn.olange.setting.SettingConfigurable; 7 | import cn.olange.utils.RuleUtil; 8 | import com.intellij.find.SearchTextArea; 9 | import com.intellij.find.actions.ShowUsagesAction; 10 | import com.intellij.icons.AllIcons; 11 | import com.intellij.ide.IdeEventQueue; 12 | import com.intellij.ide.ui.UISettings; 13 | import com.intellij.ide.util.PropertiesComponent; 14 | import com.intellij.openapi.Disposable; 15 | import com.intellij.openapi.actionSystem.ActionManager; 16 | import com.intellij.openapi.actionSystem.AnAction; 17 | import com.intellij.openapi.actionSystem.CommonShortcuts; 18 | import com.intellij.openapi.application.ApplicationManager; 19 | import com.intellij.openapi.application.ModalityState; 20 | import com.intellij.openapi.command.WriteCommandAction; 21 | import com.intellij.openapi.editor.Editor; 22 | import com.intellij.openapi.options.ShowSettingsUtil; 23 | import com.intellij.openapi.progress.ProgressIndicator; 24 | import com.intellij.openapi.progress.util.ProgressIndicatorBase; 25 | import com.intellij.openapi.progress.util.ProgressIndicatorUtils; 26 | import com.intellij.openapi.progress.util.ReadTask; 27 | import com.intellij.openapi.project.DumbAwareAction; 28 | import com.intellij.openapi.project.Project; 29 | import com.intellij.openapi.ui.DialogWrapper; 30 | import com.intellij.openapi.ui.LoadingDecorator; 31 | import com.intellij.openapi.ui.OnePixelDivider; 32 | import com.intellij.openapi.util.DimensionService; 33 | import com.intellij.openapi.util.Disposer; 34 | import com.intellij.openapi.util.SystemInfo; 35 | import com.intellij.openapi.util.registry.Registry; 36 | import com.intellij.openapi.util.text.StringUtil; 37 | import com.intellij.openapi.wm.IdeGlassPane; 38 | import com.intellij.openapi.wm.WindowManager; 39 | import com.intellij.ui.*; 40 | import com.intellij.ui.awt.RelativePoint; 41 | import com.intellij.ui.components.JBLabel; 42 | import com.intellij.ui.components.JBPanel; 43 | import com.intellij.ui.components.JBScrollPane; 44 | import com.intellij.ui.table.JBTable; 45 | import com.intellij.util.Alarm; 46 | import com.intellij.util.ui.*; 47 | import net.miginfocom.swing.MigLayout; 48 | import org.jdesktop.swingx.JXTextArea; 49 | import org.jetbrains.annotations.NotNull; 50 | import org.jetbrains.annotations.Nullable; 51 | 52 | import javax.swing.*; 53 | import javax.swing.border.Border; 54 | import javax.swing.table.DefaultTableModel; 55 | import java.awt.*; 56 | import java.awt.event.*; 57 | import java.util.List; 58 | import java.util.concurrent.atomic.AtomicBoolean; 59 | import java.util.concurrent.atomic.AtomicInteger; 60 | 61 | public class AnyRulePopupPanel extends JBPanel { 62 | private static final String IGNORE_SWAY_ROD = "any.rule.ignoreSwayRod"; 63 | private String key = "ide.anyrule.enter.as.ok"; 64 | private final Disposable myDisposable; 65 | private Project project; 66 | private Editor editor; 67 | private DialogWrapper myDialog; 68 | private JBLabel myTitleLabel; 69 | private JPanel myTitlePanel; 70 | private LoadingDecorator myLoadingDecorator; 71 | private SearchTextArea mySearchTextArea; 72 | private JXTextArea mySearchComponent; 73 | private JBTable myResultsPreviewTable; 74 | private final AtomicBoolean myCanClose; 75 | private final AtomicBoolean myIsPinned; 76 | private Alarm mySearchRescheduleOnCancellationsAlarm; 77 | private volatile ProgressIndicatorBase myResultsPreviewSearchProgress; 78 | private int myLoadingHash; 79 | private JButton myOKButton; 80 | private final Alarm myPreviewUpdater; 81 | private RulePreviewPanel rulePreviewPanel; 82 | private StateRestoringCheckBox ignoreSwayRod; 83 | public static final String SERVICE_KEY = "any.rule"; 84 | public static final String SPLITTER_SERVICE_KEY = "any.rule.splitter"; 85 | 86 | 87 | public AnyRulePopupPanel(Project project, Editor editor) { 88 | super(); 89 | this.project = project; 90 | this.editor = editor; 91 | this.myDisposable = Disposer.newDisposable(); 92 | this.myCanClose = new AtomicBoolean(true); 93 | this.myIsPinned = new AtomicBoolean(false); 94 | this.myPreviewUpdater = new Alarm(this.myDisposable); 95 | Disposer.register(this.myDisposable, () -> { 96 | this.finishPreviousPreviewSearch(); 97 | if (this.mySearchRescheduleOnCancellationsAlarm != null) { 98 | Disposer.dispose(this.mySearchRescheduleOnCancellationsAlarm); 99 | } 100 | }); 101 | initComponents(); 102 | } 103 | 104 | private void initComponents() { 105 | this.setLayout(new MigLayout("flowx, ins 4, gap 0, fillx, hidemode 3")); 106 | this.myTitleLabel = new JBLabel("anyrule", UIUtil.ComponentStyle.REGULAR); 107 | this.myTitleLabel.setFont(this.myTitleLabel.getFont().deriveFont(1)); 108 | this.myLoadingDecorator = new LoadingDecorator(new JLabel(EmptyIcon.ICON_16), this.getDisposable(), 250, true, new AsyncProcessIcon("FindInPathLoading")); 109 | this.myLoadingDecorator.setLoadingText(""); 110 | this.myTitlePanel = new JPanel(new MigLayout("flowx, ins 0, gap 0, fillx, filly")); 111 | this.myTitlePanel.add(this.myTitleLabel); 112 | this.myTitlePanel.add(this.myLoadingDecorator.getComponent(), "w 24, wmin 24"); 113 | JButton settingBtn = new JButton(AllIcons.General.Settings); 114 | settingBtn.addActionListener(new AbstractAction() { 115 | @Override 116 | public void actionPerformed(ActionEvent e) { 117 | ShowSettingsUtil.getInstance().showSettingsDialog(project, SettingConfigurable.DISPLAY_NAME); 118 | } 119 | }); 120 | this.myTitlePanel.add(Box.createHorizontalGlue(), "growx, pushx"); 121 | 122 | 123 | this.myOKButton = new JButton("双击插入"); 124 | this.myOKButton.addActionListener((e) -> { 125 | AnyRulePopupPanel.this.insertRuleToDocument(); 126 | }); 127 | 128 | this.add(this.myTitlePanel, "sx 2, growx, growx, growy"); 129 | this.add(settingBtn, "w 24, wmin 24, gapleft 4, gapright 4,wrap"); 130 | this.mySearchComponent = new JXTextArea(); 131 | this.mySearchComponent.setColumns(25); 132 | this.mySearchComponent.setRows(1); 133 | this.mySearchTextArea = new SearchTextArea(this.mySearchComponent, true, true); 134 | this.mySearchTextArea.setFocusable(true); 135 | this.mySearchTextArea.setMultilineEnabled(false); 136 | this.add(this.mySearchTextArea, "pushx, growx, sx 10, gaptop 4, wrap"); 137 | this.mySearchRescheduleOnCancellationsAlarm = new Alarm(); 138 | this.myResultsPreviewTable = new JBTable() { 139 | public Dimension getPreferredScrollableViewportSize() { 140 | return new Dimension(this.getWidth(), 1 + this.getRowHeight() * 4); 141 | } 142 | }; 143 | this.myResultsPreviewTable.setFocusable(false); 144 | this.myResultsPreviewTable.getEmptyText().setShowAboveCenter(false); 145 | this.myResultsPreviewTable.setShowColumns(false); 146 | this.myResultsPreviewTable.setShowGrid(false); 147 | this.myResultsPreviewTable.setIntercellSpacing(JBUI.emptySize()); 148 | this.myResultsPreviewTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 149 | 150 | (new DoubleClickListener() { 151 | protected boolean onDoubleClick(MouseEvent event) { 152 | if (event.getSource() != AnyRulePopupPanel.this.myResultsPreviewTable) { 153 | return false; 154 | } else { 155 | AnyRulePopupPanel.this.insertRuleToDocument(); 156 | return true; 157 | } 158 | } 159 | }).installOn(this.myResultsPreviewTable); 160 | this.myResultsPreviewTable.addMouseListener(new MouseAdapter() { 161 | public void mousePressed(MouseEvent e) { 162 | AnyRulePopupPanel.this.myResultsPreviewTable.transferFocus(); 163 | } 164 | }); 165 | ScrollingUtil.installActions(this.myResultsPreviewTable, false, mySearchComponent); 166 | 167 | JBScrollPane scrollPane = new JBScrollPane(this.myResultsPreviewTable) { 168 | public Dimension getMinimumSize() { 169 | Dimension size = super.getMinimumSize(); 170 | size.height = AnyRulePopupPanel.this.myResultsPreviewTable.getPreferredScrollableViewportSize().height; 171 | return size; 172 | } 173 | }; 174 | scrollPane.setBorder(JBUI.Borders.empty()); 175 | JBSplitter splitter = new JBSplitter(true, 0.33F); 176 | splitter.setSplitterProportionKey(SPLITTER_SERVICE_KEY); 177 | splitter.setDividerWidth(1); 178 | splitter.getDivider().setBackground(OnePixelDivider.BACKGROUND); 179 | splitter.setFirstComponent(scrollPane); 180 | this.rulePreviewPanel = new RulePreviewPanel(this.project) { 181 | public Dimension getPreferredSize() { 182 | return new Dimension(AnyRulePopupPanel.this.myResultsPreviewTable.getWidth(), this.getHeight()); 183 | } 184 | }; 185 | Disposer.register(this.myDisposable, rulePreviewPanel); 186 | 187 | final Runnable updatePreviewRunnable = () -> { 188 | if (!Disposer.isDisposed(this.myDisposable)) { 189 | int selectedRow = this.myResultsPreviewTable.getSelectedRow(); 190 | if (selectedRow >= 0) { 191 | Object data = this.myResultsPreviewTable.getModel().getValueAt(selectedRow, 0); 192 | rulePreviewPanel.updateLayout((RuleModel) data); 193 | } 194 | } 195 | }; 196 | 197 | splitter.setSecondComponent(this.rulePreviewPanel); 198 | this.rulePreviewPanel.setVisible(false); 199 | this.myResultsPreviewTable.getSelectionModel().addListSelectionListener((e) -> { 200 | if (!e.getValueIsAdjusting() && !Disposer.isDisposed(this.myPreviewUpdater)) { 201 | this.myPreviewUpdater.addRequest(updatePreviewRunnable, 50); 202 | } 203 | }); 204 | this.add(splitter, "pushx, growx, growy, pushy, sx 10, wrap, pad 4 4 4 4"); 205 | DocumentAdapter documentAdapter = new DocumentAdapter() { 206 | protected void textChanged(@NotNull javax.swing.event.DocumentEvent e) { 207 | AnyRulePopupPanel.this.mySearchComponent.setRows(Math.max(1, Math.min(1, StringUtil.countChars(AnyRulePopupPanel.this.mySearchComponent.getText(), '\n') + 1))); 208 | if (AnyRulePopupPanel.this.myDialog != null) { 209 | if (e.getDocument() == AnyRulePopupPanel.this.mySearchComponent.getDocument()) { 210 | AnyRulePopupPanel.this.scheduleResultsUpdate(); 211 | } 212 | } 213 | } 214 | }; 215 | this.mySearchComponent.getDocument().addDocumentListener(documentAdapter); 216 | this.ignoreSwayRod = createCheckBox("自动去除首尾斜杆"); 217 | PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(this.project); 218 | boolean ignore = propertiesComponent.getBoolean(IGNORE_SWAY_ROD, false); 219 | this.ignoreSwayRod.setSelected(ignore); 220 | this.ignoreSwayRod.addItemListener(new ItemListener() { 221 | @Override 222 | public void itemStateChanged(ItemEvent e) { 223 | propertiesComponent.setValue(IGNORE_SWAY_ROD, ignoreSwayRod.isSelected()); 224 | } 225 | }); 226 | JPanel bottomPanel = new JPanel(new BorderLayout()); 227 | bottomPanel.add(ignoreSwayRod, BorderLayout.CENTER); 228 | bottomPanel.add(myOKButton, BorderLayout.EAST); 229 | this.add(bottomPanel, "pushx, growx, dock south, sx 10"); 230 | } 231 | 232 | @NotNull 233 | private static StateRestoringCheckBox createCheckBox(String message) { 234 | StateRestoringCheckBox checkBox = new StateRestoringCheckBox(message); 235 | checkBox.setFocusable(false); 236 | return checkBox; 237 | } 238 | 239 | public void scheduleResultsUpdate() { 240 | if (this.myDialog != null && this.myDialog.isVisible()) { 241 | if (this.mySearchRescheduleOnCancellationsAlarm != null && !this.mySearchRescheduleOnCancellationsAlarm.isDisposed()) { 242 | this.mySearchRescheduleOnCancellationsAlarm.cancelAllRequests(); 243 | this.mySearchRescheduleOnCancellationsAlarm.addRequest(this::findSettingsChanged, 100); 244 | } 245 | } 246 | } 247 | 248 | public void showUI() { 249 | if (this.myDialog == null || !this.myDialog.isVisible()) { 250 | if (this.myDialog != null && !Disposer.isDisposed(this.myDialog.getDisposable())) { 251 | this.myDialog.doCancelAction(); 252 | } 253 | 254 | if (this.myDialog == null || Disposer.isDisposed(this.myDialog.getDisposable())) { 255 | myDialog = new DialogWrapper(this.project, true, DialogWrapper.IdeModalityType.MODELESS) { 256 | { 257 | init(); 258 | getContentPane().add(new JLabel(), BorderLayout.SOUTH);//remove hardcoded southSection 259 | getRootPane().setDefaultButton(null); 260 | } 261 | 262 | 263 | @Override 264 | protected void dispose() { 265 | super.dispose(); 266 | } 267 | 268 | @NotNull 269 | @Override 270 | protected Action[] createLeftSideActions() { 271 | return new Action[0]; 272 | } 273 | 274 | @NotNull 275 | @Override 276 | protected Action[] createActions() { 277 | return new Action[0]; 278 | } 279 | 280 | @Nullable 281 | @Override 282 | protected Border createContentPaneBorder() { 283 | return null; 284 | } 285 | 286 | @Override 287 | protected JComponent createCenterPanel() { 288 | return AnyRulePopupPanel.this; 289 | } 290 | 291 | @Override 292 | protected String getDimensionServiceKey() { 293 | return SERVICE_KEY; 294 | } 295 | }; 296 | this.myDialog.setUndecorated(true); 297 | Disposer.register(project, AnyRulePopupPanel.this::closeImmediately); 298 | Disposer.register(this.myDialog.getDisposable(), this.myDisposable); 299 | Window window = WindowManager.getInstance().suggestParentWindow(this.project); 300 | Component parent = UIUtil.findUltimateParent(window); 301 | RelativePoint showPoint = null; 302 | Point screenPoint = DimensionService.getInstance().getLocation(SERVICE_KEY); 303 | if (screenPoint != null) { 304 | if (parent != null) { 305 | SwingUtilities.convertPointFromScreen(screenPoint, parent); 306 | showPoint = new RelativePoint(parent, screenPoint); 307 | } else { 308 | showPoint = new RelativePoint(screenPoint); 309 | } 310 | } 311 | 312 | if (parent != null && showPoint == null) { 313 | int height = UISettings.getInstance().getShowNavigationBar() ? 135 : 115; 314 | showPoint = new RelativePoint(parent, new Point((parent.getSize().width - this.getPreferredSize().width) / 2, height)); 315 | } 316 | 317 | WindowMoveListener windowListener = new WindowMoveListener(this); 318 | this.addMouseListener(windowListener); 319 | this.addMouseMotionListener(windowListener); 320 | Dimension panelSize = this.getPreferredSize(); 321 | Dimension prev = DimensionService.getInstance().getSize(SERVICE_KEY); 322 | 323 | panelSize.width += JBUI.scale(24); 324 | panelSize.height *= 2; 325 | if (prev != null && prev.height < panelSize.height) { 326 | prev.height = panelSize.height; 327 | } 328 | 329 | final Window w = this.myDialog.getPeer().getWindow(); 330 | AnAction escape = ActionManager.getInstance().getAction("EditorEscape"); 331 | JRootPane root = ((RootPaneContainer) w).getRootPane(); 332 | final IdeGlassPane glass = (IdeGlassPane) this.myDialog.getRootPane().getGlassPane(); 333 | int i = Registry.intValue("ide.popup.resizable.border.sensitivity", 4); 334 | WindowResizeListener resizeListener = new WindowResizeListener(root, JBUI.insets(i), (Icon) null) { 335 | private Cursor myCursor; 336 | 337 | protected void setCursor(Component content, Cursor cursor) { 338 | if (this.myCursor != cursor || this.myCursor != Cursor.getDefaultCursor()) { 339 | glass.setCursor(cursor, this); 340 | this.myCursor = cursor; 341 | if (content instanceof JComponent) { 342 | JComponent component = (JComponent) content; 343 | if (component.getClientProperty("SuperCursor") == null) { 344 | component.putClientProperty("SuperCursor", cursor); 345 | } 346 | } 347 | super.setCursor(content, cursor); 348 | } 349 | 350 | } 351 | }; 352 | glass.addMousePreprocessor(resizeListener, this.myDisposable); 353 | glass.addMouseMotionPreprocessor(resizeListener, this.myDisposable); 354 | DumbAwareAction.create((e) -> { 355 | AnyRulePopupPanel.this.closeImmediately(); 356 | }).registerCustomShortcutSet(escape == null ? CommonShortcuts.ESCAPE : escape.getShortcutSet(), root, this.myDisposable); 357 | root.setWindowDecorationStyle(0); 358 | if (SystemInfo.isMac && UIUtil.isUnderDarcula()) { 359 | root.setBorder(PopupBorder.Factory.createColored(OnePixelDivider.BACKGROUND)); 360 | } else { 361 | root.setBorder(PopupBorder.Factory.create(true, true)); 362 | } 363 | 364 | w.setBackground(UIUtil.getPanelBackground()); 365 | w.setMinimumSize(panelSize); 366 | if (prev == null) { 367 | panelSize.height = (int) ((double) panelSize.height * 1.5D); 368 | panelSize.width = (int) ((double) panelSize.width * 1.15D); 369 | } 370 | 371 | w.setSize(prev != null ? prev : panelSize); 372 | IdeEventQueue.getInstance().getPopupManager().closeAllPopups(false); 373 | if (showPoint != null) { 374 | this.myDialog.setLocation(showPoint.getScreenPoint()); 375 | } else { 376 | w.setLocationRelativeTo(parent); 377 | } 378 | this.myDialog.show(); 379 | this.mySearchComponent.requestFocus(); 380 | w.addWindowListener(new WindowAdapter() { 381 | public void windowOpened(WindowEvent e) { 382 | w.addWindowFocusListener(new WindowAdapter() { 383 | public void windowLostFocus(WindowEvent e) { 384 | Window oppositeWindow = e.getOppositeWindow(); 385 | if (oppositeWindow != w && (oppositeWindow == null || oppositeWindow.getOwner() != w)) { 386 | if (AnyRulePopupPanel.this.canBeClosed() || !AnyRulePopupPanel.this.myIsPinned.get() && oppositeWindow != null) { 387 | AnyRulePopupPanel.this.myDialog.doCancelAction(); 388 | } 389 | 390 | } 391 | } 392 | }); 393 | } 394 | }); 395 | } 396 | ApplicationManager.getApplication().invokeLater(this::scheduleResultsUpdate, ModalityState.any()); 397 | } 398 | } 399 | 400 | @NotNull 401 | public Disposable getDisposable() { 402 | return this.myDisposable; 403 | } 404 | 405 | private void closeImmediately() { 406 | if (this.canBeClosedImmediately() && this.myDialog != null && this.myDialog.isVisible()) { 407 | this.myIsPinned.set(false); 408 | this.myDialog.doCancelAction(); 409 | } 410 | 411 | } 412 | 413 | public void insertRuleToDocument() { 414 | if (this.canBeClosed()) { 415 | this.myDialog.doCancelAction(); 416 | } 417 | int selectedRow = this.myResultsPreviewTable.getSelectedRow(); 418 | if (selectedRow < 0) { 419 | return; 420 | } 421 | if (this.editor == null) { 422 | return; 423 | } 424 | RuleModel value = (RuleModel) this.myResultsPreviewTable.getModel().getValueAt(selectedRow, 0); 425 | ApplicationManager.getApplication().runWriteAction(() -> { 426 | int offset = editor.getCaretModel().getOffset(); 427 | WriteCommandAction.Builder builder = WriteCommandAction.writeCommandAction(AnyRulePopupPanel.this.project); 428 | builder.run(() -> { 429 | String rule = value.getRule(); 430 | editor.getDocument().insertString(offset, convertRule(rule)); 431 | }); 432 | }); 433 | } 434 | 435 | private String convertRule(String rule) { 436 | if (rule == null) { 437 | return ""; 438 | } 439 | if (this.ignoreSwayRod.isSelected()) { 440 | return RuleUtil.convertRule(rule); 441 | } 442 | return rule; 443 | 444 | 445 | } 446 | 447 | private boolean canBeClosedImmediately() { 448 | boolean state = this.myIsPinned.get(); 449 | this.myIsPinned.set(false); 450 | 451 | boolean var2; 452 | try { 453 | var2 = this.myDialog != null && this.canBeClosed(); 454 | } finally { 455 | this.myIsPinned.set(state); 456 | } 457 | 458 | return var2; 459 | } 460 | 461 | protected boolean canBeClosed() { 462 | if (this.project.isDisposed()) { 463 | return true; 464 | } else if (!this.myCanClose.get()) { 465 | return false; 466 | } else if (this.myIsPinned.get()) { 467 | return false; 468 | } else if (!ApplicationManager.getApplication().isActive()) { 469 | return false; 470 | } else if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow() == null) { 471 | return false; 472 | } else { 473 | return true; 474 | } 475 | } 476 | 477 | private void showEmptyText(@Nullable String message) { 478 | StatusText emptyText = this.myResultsPreviewTable.getEmptyText(); 479 | emptyText.clear(); 480 | emptyText.setText(message != null ? UIBundle.message("message.nothingToShow.with.problem", new Object[]{message}) : UIBundle.message("message.nothingToShow", new Object[0])); 481 | } 482 | 483 | private void finishPreviousPreviewSearch() { 484 | if (this.myResultsPreviewSearchProgress != null && !this.myResultsPreviewSearchProgress.isCanceled()) { 485 | this.myResultsPreviewSearchProgress.cancel(); 486 | } 487 | 488 | } 489 | 490 | private void findSettingsChanged() { 491 | if (this.isShowing()) { 492 | ScrollingUtil.ensureSelectionExists(this.myResultsPreviewTable); 493 | } 494 | final ModalityState state = ModalityState.current(); 495 | this.finishPreviousPreviewSearch(); 496 | this.mySearchRescheduleOnCancellationsAlarm.cancelAllRequests(); 497 | final ProgressIndicatorBase progressIndicatorWhenSearchStarted = new ProgressIndicatorBase() { 498 | public void stop() { 499 | super.stop(); 500 | AnyRulePopupPanel.this.onStop(System.identityHashCode(this)); 501 | } 502 | }; 503 | this.myResultsPreviewSearchProgress = progressIndicatorWhenSearchStarted; 504 | final int hash = System.identityHashCode(this.myResultsPreviewSearchProgress); 505 | final DefaultTableModel model = new DefaultTableModel() { 506 | public boolean isCellEditable(int row, int column) { 507 | return false; 508 | } 509 | }; 510 | model.addColumn("Usages"); 511 | this.mySearchTextArea.setInfoText(null); 512 | this.myResultsPreviewTable.setModel(model); 513 | this.myResultsPreviewTable.getColumnModel().getColumn(0).setCellRenderer(new UsageTableCellRenderer()); 514 | this.onStart(hash); 515 | final AtomicInteger resultsCount = new AtomicInteger(); 516 | ProgressIndicatorUtils.scheduleWithWriteActionPriority(this.myResultsPreviewSearchProgress, new ReadTask() { 517 | public ReadTask.Continuation performInReadAction(@NotNull ProgressIndicator indicator) { 518 | if (this.isCancelled()) { 519 | AnyRulePopupPanel.this.onStop(hash); 520 | } else { 521 | ApplicationManager.getApplication().invokeLater(() -> { 522 | if (this.isCancelled()) { 523 | AnyRulePopupPanel.this.onStop(hash); 524 | } else { 525 | RuleDataService.filterRule(AnyRulePopupPanel.this.mySearchComponent.getText(), (result) -> { 526 | if (result.isSuccess()) { 527 | List array = result.getResult(); 528 | for (int i = 0; i < array.size(); i++) { 529 | RuleModel rule = array.get(i); 530 | model.insertRow(i, new Object[]{rule}); 531 | } 532 | int occurrences = resultsCount.get(); 533 | if (model.getRowCount() > 0 && AnyRulePopupPanel.this.myResultsPreviewTable.getModel() == model) { 534 | AnyRulePopupPanel.this.rulePreviewPanel.setVisible(occurrences > 0); 535 | AnyRulePopupPanel.this.myResultsPreviewTable.setRowSelectionInterval(0, 0); 536 | } else { 537 | AnyRulePopupPanel.this.rulePreviewPanel.setVisible(false); 538 | } 539 | } 540 | }); 541 | } 542 | }, state); 543 | boolean continueSearch = resultsCount.incrementAndGet() < ShowUsagesAction.getUsagesPageSize(); 544 | if (!continueSearch) { 545 | AnyRulePopupPanel.this.onStop(hash); 546 | } 547 | } 548 | return new Continuation(() -> { 549 | if (!this.isCancelled() && resultsCount.get() == 0) { 550 | AnyRulePopupPanel.this.showEmptyText(null); 551 | } 552 | 553 | AnyRulePopupPanel.this.onStop(hash); 554 | }, state); 555 | } 556 | 557 | boolean isCancelled() { 558 | return progressIndicatorWhenSearchStarted != AnyRulePopupPanel.this.myResultsPreviewSearchProgress || progressIndicatorWhenSearchStarted.isCanceled(); 559 | } 560 | 561 | public void onCanceled(@NotNull ProgressIndicator indicator) { 562 | if (AnyRulePopupPanel.this.isShowing() && progressIndicatorWhenSearchStarted == AnyRulePopupPanel.this.myResultsPreviewSearchProgress) { 563 | AnyRulePopupPanel.this.scheduleResultsUpdate(); 564 | } 565 | } 566 | }); 567 | } 568 | 569 | private void onStart(int hash) { 570 | this.myLoadingHash = hash; 571 | this.myLoadingDecorator.startLoading(false); 572 | this.myResultsPreviewTable.getEmptyText().setText("Searching..."); 573 | } 574 | 575 | private void onStop(int hash) { 576 | this.onStop(hash, null); 577 | } 578 | 579 | private void onStop(int hash, String message) { 580 | if (hash == this.myLoadingHash) { 581 | UIUtil.invokeLaterIfNeeded(() -> { 582 | this.showEmptyText(message); 583 | this.myLoadingDecorator.stopLoading(); 584 | }); 585 | } 586 | } 587 | } 588 | --------------------------------------------------------------------------------