├── ReactPropTypes.jar ├── ScreenShots ├── ScreenShot1.gif ├── ScreenShot2.gif ├── ScreenShot3.gif └── ScreenShot4.gif ├── src └── com │ └── suming │ └── plugin │ ├── bean │ ├── ESVersion.java │ ├── ComponentType.java │ ├── ImportMode.java │ ├── Component.java │ ├── BasePropType.java │ ├── PropTypeBean.java │ └── Setting.java │ ├── constants │ ├── ShapePropTypes.java │ ├── SpecialPropTypes.java │ └── ArrayFunctions.java │ ├── ui │ ├── ButtonRenderer.java │ ├── CheckBoxRenderer.java │ ├── ComboBoxRenderer.java │ ├── ButtonEditor.java │ ├── NameTextRenderer.java │ ├── ShapePropTypesModel.java │ ├── config │ │ ├── SettingEntry.java │ │ ├── SettingForm.java │ │ └── SettingForm.form │ ├── JsonInputDialog.java │ ├── PropTypesModel.java │ ├── JsonInputDialog.form │ ├── ShapePropTypesDialog.form │ ├── ShapePropTypesDialog.java │ ├── PropTypesDialog.form │ └── PropTypesDialog.java │ ├── utils │ ├── IdeaCompat.java │ ├── SelectWordUtilCompat.java │ ├── PsiElementHelper.java │ └── PropTypesHelper.java │ ├── persist │ └── SettingService.java │ ├── PropTypeAction.java │ └── CommonAction.java ├── .gitignore ├── ReactPropTypes.iml ├── LICENSE ├── README_ZH.md ├── resources └── META-INF │ └── plugin.xml └── README.md /ReactPropTypes.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpzxsm/ReactPropTypes-Plugin/HEAD/ReactPropTypes.jar -------------------------------------------------------------------------------- /ScreenShots/ScreenShot1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpzxsm/ReactPropTypes-Plugin/HEAD/ScreenShots/ScreenShot1.gif -------------------------------------------------------------------------------- /ScreenShots/ScreenShot2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpzxsm/ReactPropTypes-Plugin/HEAD/ScreenShots/ScreenShot2.gif -------------------------------------------------------------------------------- /ScreenShots/ScreenShot3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpzxsm/ReactPropTypes-Plugin/HEAD/ScreenShots/ScreenShot3.gif -------------------------------------------------------------------------------- /ScreenShots/ScreenShot4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dpzxsm/ReactPropTypes-Plugin/HEAD/ScreenShots/ScreenShot4.gif -------------------------------------------------------------------------------- /src/com/suming/plugin/bean/ESVersion.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.bean; 2 | 3 | public enum ESVersion { 4 | ES6, 5 | ES7, 6 | } 7 | -------------------------------------------------------------------------------- /src/com/suming/plugin/constants/ShapePropTypes.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.constants; 2 | 3 | public enum ShapePropTypes { 4 | exact, 5 | shape 6 | } 7 | -------------------------------------------------------------------------------- /src/com/suming/plugin/bean/ComponentType.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.bean; 2 | 3 | public enum ComponentType { 4 | STANDARD_ES5, 5 | STANDARD, 6 | STATELESS 7 | } 8 | -------------------------------------------------------------------------------- /src/com/suming/plugin/constants/SpecialPropTypes.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.constants; 2 | 3 | public enum SpecialPropTypes { 4 | arrayOf, 5 | objectOf, 6 | oneOf, 7 | instanceOf, 8 | oneOfType 9 | } 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 | *.war 15 | *.ear 16 | *.zip 17 | *.tar.gz 18 | *.rar 19 | 20 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 21 | hs_err_pid* 22 | .idea 23 | -------------------------------------------------------------------------------- /src/com/suming/plugin/constants/ArrayFunctions.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.constants; 2 | 3 | public enum ArrayFunctions { 4 | concat, 5 | every, 6 | filter, 7 | find, 8 | findIndex, 9 | flat, 10 | flatMap, 11 | forEach, 12 | includes, 13 | join, 14 | map, 15 | reduce, 16 | reverse, 17 | slice, 18 | some, 19 | sort, 20 | splice, 21 | unshift, 22 | } 23 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/ButtonRenderer.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.TableCellRenderer; 5 | import java.awt.*; 6 | 7 | public class ButtonRenderer extends JButton implements TableCellRenderer { 8 | 9 | @Override 10 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 11 | setText("delete"); 12 | return this; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ReactPropTypes.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/CheckBoxRenderer.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import com.intellij.ui.components.JBCheckBox; 4 | 5 | import javax.swing.*; 6 | import javax.swing.table.TableCellRenderer; 7 | import java.awt.*; 8 | 9 | public class CheckBoxRenderer extends JBCheckBox implements TableCellRenderer{ 10 | 11 | CheckBoxRenderer() { 12 | this.setHorizontalAlignment(SwingConstants.CENTER); 13 | } 14 | 15 | @Override 16 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 17 | this.setSelected(value.toString().equals("true")); 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/com/suming/plugin/utils/IdeaCompat.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.utils; 2 | 3 | import com.intellij.openapi.application.ApplicationInfo; 4 | 5 | @SuppressWarnings({"SpellCheckingInspection", "WeakerAccess", "unused"}) 6 | public final class IdeaCompat { 7 | 8 | public static final int BUILD_NUMBER = ApplicationInfo.getInstance().getBuild().getBaselineVersion(); 9 | 10 | public static final class Version { 11 | public static final int IDEA15 = 143; 12 | public static final int IDEA2016_1 = 145; 13 | public static final int IDEA2016_2 = 162; 14 | public static final int IDEA2016_3 = 163; 15 | public static final int IDEA2017_1 = 171; 16 | 17 | private Version() { 18 | } 19 | } 20 | 21 | private IdeaCompat() { 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/com/suming/plugin/bean/ImportMode.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.bean; 2 | 3 | public enum ImportMode { 4 | Disabled("Disabled"), 5 | OldModules("React.PropTypes"), 6 | NewModules("prop-types"); 7 | 8 | private final String value; 9 | 10 | ImportMode(String value) { 11 | this.value = value; 12 | } 13 | 14 | public String getValue() { 15 | return value; 16 | } 17 | 18 | public static ImportMode toEnum(String value){ 19 | if(value == null) return null; 20 | if(value.equals(Disabled.value)){ 21 | return Disabled; 22 | }else if(value.equals(OldModules.value)){ 23 | return OldModules; 24 | }else if(value.equals(NewModules.value)){ 25 | return NewModules; 26 | }else { 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/com/suming/plugin/persist/SettingService.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.persist; 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent; 4 | import com.intellij.openapi.components.State; 5 | import com.intellij.openapi.components.Storage; 6 | import com.suming.plugin.bean.Setting; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | @State(name = "SettingVariables", 10 | storages = { 11 | @Storage("Setting.xml") 12 | } 13 | ) 14 | 15 | public class SettingService implements PersistentStateComponent { 16 | private Setting mSetting = new Setting(); 17 | 18 | @NotNull 19 | @Override 20 | public Setting getState() { 21 | return mSetting; 22 | } 23 | 24 | @Override 25 | public void loadState(Setting setting) { 26 | this.mSetting = setting; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 苏铭 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 | -------------------------------------------------------------------------------- /src/com/suming/plugin/bean/Component.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.bean; 2 | 3 | import com.intellij.psi.PsiElement; 4 | 5 | public class Component { 6 | private PsiElement element; 7 | private ComponentType componentType; 8 | private ESVersion esVersion; 9 | 10 | public Component(PsiElement element, ComponentType componentType ) { 11 | this.element = element; 12 | this.componentType = componentType; 13 | } 14 | 15 | public Component(PsiElement element, ComponentType componentType, ESVersion esVersion) { 16 | this.element = element; 17 | this.componentType = componentType; 18 | this.esVersion = esVersion; 19 | } 20 | 21 | public PsiElement getElement() { 22 | return element; 23 | } 24 | 25 | public ComponentType getComponentType() { 26 | return componentType; 27 | } 28 | 29 | public ESVersion getEsVersion() { 30 | return esVersion; 31 | } 32 | 33 | public void setElement(PsiElement element) { 34 | this.element = element; 35 | } 36 | 37 | public void setComponentType(ComponentType componentType) { 38 | this.componentType = componentType; 39 | } 40 | 41 | public void setEsVersion(ESVersion esVersion) { 42 | this.esVersion = esVersion; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/com/suming/plugin/bean/BasePropType.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.bean; 2 | 3 | public class BasePropType { 4 | public String name; 5 | public String type; 6 | public boolean isRequired; 7 | 8 | // BasePropType's JSON String or the Other value 9 | private String jsonData; 10 | 11 | public BasePropType(String name, String type, boolean isRequired) { 12 | this.name = name; 13 | this.type = type; 14 | this.isRequired = isRequired; 15 | } 16 | 17 | public String getName() { 18 | return name; 19 | } 20 | 21 | public void setName(String name) { 22 | this.name = name; 23 | } 24 | 25 | public String getType() { 26 | return type; 27 | } 28 | 29 | public void setType(String type) { 30 | if (type != null && !type.equals("")) { 31 | this.type = type; 32 | } else { 33 | this.type = "any"; 34 | } 35 | } 36 | 37 | public boolean isRequired() { 38 | return isRequired; 39 | } 40 | 41 | public void setRequired(boolean required) { 42 | isRequired = required; 43 | } 44 | 45 | public String getJsonData() { 46 | return jsonData; 47 | } 48 | 49 | public void setJsonData(String jsonData) { 50 | this.jsonData = jsonData; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/ComboBoxRenderer.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.TableCellRenderer; 5 | import java.awt.*; 6 | 7 | public class ComboBoxRenderer extends JComboBox implements TableCellRenderer { 8 | 9 | ComboBoxRenderer(boolean showAllType) { 10 | super(new String[]{ 11 | "any", 12 | "string", 13 | "object", 14 | "bool", 15 | "func", 16 | "number", 17 | "array", 18 | "symbol", 19 | "node", 20 | "element", 21 | "arrayOf", 22 | "objectOf", 23 | "oneOf", 24 | "instanceOf", 25 | "oneOfType" 26 | }); 27 | if (showAllType) { 28 | this.insertItemAt("shape", 10); 29 | this.insertItemAt("exact", 11); 30 | } 31 | this.setMaximumRowCount(10); 32 | this.setEditable(false); 33 | this.setLightWeightPopupEnabled(false); 34 | } 35 | 36 | @Override 37 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 38 | this.setSelectedItem(value.toString()); 39 | return this; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/com/suming/plugin/bean/PropTypeBean.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.bean; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.List; 6 | 7 | public class PropTypeBean extends BasePropType { 8 | // default value 9 | private String defaultValue; 10 | // shapeList 11 | private List shapePropTypeList; 12 | 13 | public PropTypeBean(String name) { 14 | super(name, "any", false); 15 | } 16 | 17 | public PropTypeBean(String name, String type, boolean isRequired) { 18 | super(name, type, isRequired); 19 | } 20 | 21 | 22 | public PropTypeBean(String name, String type, boolean isRequired, String defaultValue) { 23 | super(name, type, isRequired); 24 | this.defaultValue = defaultValue; 25 | } 26 | 27 | @Nullable 28 | public String getDefaultValue() { 29 | return defaultValue; 30 | } 31 | 32 | public void setDefaultValue(String defaultValue) { 33 | this.defaultValue = defaultValue; 34 | } 35 | 36 | public List getShapePropTypeList() { 37 | return shapePropTypeList; 38 | } 39 | 40 | public void setShapePropTypeList(List shapePropTypeList) { 41 | this.shapePropTypeList = shapePropTypeList; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object obj) { 46 | if (obj instanceof PropTypeBean) { 47 | return ((PropTypeBean) obj).name.equals(this.name); 48 | } 49 | return super.equals(obj); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/suming/plugin/utils/SelectWordUtilCompat.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.utils; 2 | 3 | import com.intellij.codeInsight.editorActions.SelectWordUtil; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.util.TextRange; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.List; 9 | 10 | @SuppressWarnings("SpellCheckingInspection") 11 | public final class SelectWordUtilCompat { 12 | 13 | private SelectWordUtilCompat() { 14 | } 15 | 16 | public static final SelectWordUtil.CharCondition JAVASCRIPT_IDENTIFIER_PART_CONDITION = new SelectWordUtil.CharCondition() { 17 | public boolean value(char var1) { 18 | return var1 != ' '; 19 | } 20 | }; 21 | 22 | 23 | public static void addWordOrLexemeSelection(boolean camel, 24 | @NotNull Editor editor, 25 | int cursorOffset, 26 | @NotNull List ranges, 27 | @NotNull SelectWordUtil.CharCondition isWordPartCondition) { 28 | if (IdeaCompat.BUILD_NUMBER >= IdeaCompat.Version.IDEA2016_2) { 29 | SelectWordUtil.addWordOrLexemeSelection(camel, editor, cursorOffset, ranges, isWordPartCondition); 30 | } else { 31 | CharSequence editorText = editor.getDocument().getImmutableCharSequence(); 32 | SelectWordUtil.addWordSelection(camel, editorText, cursorOffset, ranges, isWordPartCondition); 33 | } 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/ButtonEditor.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.DefaultTableModel; 5 | import java.awt.*; 6 | import java.awt.event.ActionEvent; 7 | import java.awt.event.ActionListener; 8 | import java.util.EventObject; 9 | 10 | public class ButtonEditor extends DefaultCellEditor{ 11 | 12 | private JButton editor; 13 | private Object value; 14 | private int row; 15 | private JTable table; 16 | 17 | ButtonEditor() { 18 | super(new JTextField()); 19 | editor = new JButton(); 20 | editor.addActionListener(e -> { 21 | //这里调用自定义的事件处理方法 22 | if (table != null && table.getModel() instanceof DefaultTableModel) { 23 | fireEditingStopped(); 24 | ((DefaultTableModel)table.getModel()).removeRow(row); 25 | } 26 | }); 27 | 28 | } 29 | 30 | @Override 31 | public boolean isCellEditable(EventObject e) { 32 | return true; 33 | } 34 | 35 | @Override 36 | public Object getCellEditorValue() { 37 | return value; 38 | } 39 | 40 | @Override 41 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 42 | this.table = table; 43 | this.row = row; 44 | this.value = value; 45 | editor.setText("delete"); 46 | 47 | // if (isSelected) { 48 | // editor.setForeground(table.getSelectionForeground()); 49 | // editor.setBackground(table.getSelectionBackground()); 50 | // } else { 51 | // editor.setForeground(table.getForeground()); 52 | // editor.setBackground(UIManager.getColor("Button.background")); 53 | // } 54 | return editor; 55 | } 56 | } -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/NameTextRenderer.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import com.intellij.ui.JBColor; 4 | 5 | import javax.swing.*; 6 | import javax.swing.table.TableCellRenderer; 7 | import java.awt.*; 8 | import java.awt.event.FocusAdapter; 9 | import java.awt.event.FocusEvent; 10 | 11 | public class NameTextRenderer extends JTextField implements TableCellRenderer{ 12 | 13 | private String placeholder = ""; 14 | 15 | NameTextRenderer(boolean isCellRenderer , String placeholder) { 16 | super(); 17 | this.placeholder = placeholder; 18 | if(isCellRenderer){ 19 | setBorder(null); 20 | setBackground(null); 21 | } 22 | this.addFocusListener(new FocusAdapter() { 23 | @Override 24 | public void focusGained(FocusEvent e) { 25 | if(getText().equals(placeholder)){ 26 | setText(""); 27 | } 28 | super.focusGained(e); 29 | } 30 | 31 | @Override 32 | public void focusLost(FocusEvent e) { 33 | super.focusLost(e); 34 | if(getText().equals("")){ 35 | setText(placeholder); 36 | } 37 | } 38 | }); 39 | } 40 | 41 | @Override 42 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 43 | String content = value == null ? "" : value.toString(); 44 | if(content.trim().equals("")||content.equals(placeholder)){ 45 | setText(placeholder); 46 | setForeground(JBColor.GRAY); 47 | }else { 48 | setText(content); 49 | setForeground(null); 50 | } 51 | return this; 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/com/suming/plugin/utils/PsiElementHelper.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.utils; 2 | 3 | import com.intellij.lang.ecmascript6.psi.ES6Class; 4 | import com.intellij.lang.javascript.psi.JSFunction; 5 | import com.intellij.lang.javascript.psi.impl.JSVarStatementBase; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiWhiteSpace; 8 | 9 | public class PsiElementHelper { 10 | public static PsiElement getRealPreElement (PsiElement p){ 11 | PsiElement p1 = p.getPrevSibling(); 12 | if(p1 !=null){ 13 | if(!(p1 instanceof PsiWhiteSpace)){ 14 | return p1; 15 | } 16 | PsiElement p2 = p1.getPrevSibling(); 17 | if(p2 !=null && !( p2 instanceof PsiWhiteSpace)){ 18 | return p2; 19 | } 20 | } 21 | return null; 22 | } 23 | 24 | public static PsiElement getRealNextElement (PsiElement p){ 25 | PsiElement p1 = p.getNextSibling(); 26 | if(p1 !=null){ 27 | if(!(p1 instanceof PsiWhiteSpace)){ 28 | return p1; 29 | } 30 | PsiElement p2 = p1.getNextSibling(); 31 | if(p2 !=null && !( p2 instanceof PsiWhiteSpace)){ 32 | return p2; 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | public static PsiElement getRealFirstChild (ES6Class es6Class){ 39 | PsiElement[] children = es6Class.getChildren(); 40 | if(children.length>2){ 41 | if((children[2] instanceof JSFunction)|| children[2] instanceof JSVarStatementBase){ 42 | return children[2]; 43 | } 44 | } 45 | if(children.length>3){ 46 | if((children[3] instanceof JSFunction)|| children[3] instanceof JSVarStatementBase){ 47 | return children[3]; 48 | } 49 | } 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | 这是一个可以自动生成React组件的PropTypes代码的jetbrains插件,目前仅支持ES6、ES7。如果需要支持ES5,请在issue中留言。支持的平台有:IntelliJ IDEA、PhpStorm、WebStorm、PyCharm、RubyMine、AppCode、CLion、Gogland、Rider 2 | 3 | 如果你想在VS Code或者通过命令行使用这个插件,建议你去使用另一个插件: [react-proptypes-generate](https://github.com/dpzxsm/react-proptypes-generate), 现在并不是很完美,欢迎issue和PR 4 | 5 | ## 安装插件 6 | 1. 在插件商店中搜索ReactPropTypes下载安装,这是商店链接,欢迎评论. 7 | 2. 点击 ReactPropTypes.jar(最新版本,但可能不太稳定) 下载插件并且打开Setting/Plugins/Install Plugin from disk 本地安装这个插件 8 | 9 | ## 如何使用 10 | 1. 选择组件名称 11 | 2. 按下 command + N (Windows系统是alt + insert) 并且选择PropTypesGenerate, 或者按下shift + command + alt + P (Windows系统是shift + ctrl + alt + P) 在Mac系统中。 12 | 3. 编辑弹框中的表格进行类型的修改稿 13 | 14 | ## 15 | #### 类组件 16 | ![img](./ScreenShots/ScreenShot1.gif) 17 | #### 函数组件 18 | ![img](./ScreenShots/ScreenShot2.gif) 19 | #### 当你选择了`Shape`作为类型 20 | ![img](./ScreenShots/ScreenShot3.gif) 21 | 22 | ## 如何构建和开发这个项目 23 | 24 | #### 依赖库 25 | * IntelliJ Platform Plugin SDK (官方插件SDK) 26 | * JavaScriptLanguage's lib (在macOS中, 路径是 /Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/lib, Windows类似) 27 | 28 | #### 构建步骤 29 | 1. 下载最新的IntelliJ IDEA商业版 30 | 2. Git拷贝项目并且用IDEA打开 31 | 3. 删除默认的module,并且通过`ReactPropTypes.iml`这个文件来导入module 32 | 4. 配置IntelliJ Platform Plugin SDK 和 language level 8 33 | 5. 配置插件的构建目录 34 | 35 | #### 示例 36 | ![img](./ScreenShots/ScreenShot4.gif) 37 | 38 | ## 特性 39 | 1. 如果你没有选择任何文字,插件将自动找到高亮的文字作为选择的组件名称。 40 | 1. 在ES6的标准组件中,插件通过找到以props和nextProps为对象名称的引用和解析赋值来找到属性名。 41 | 2. 在无状态组件中,只有当你的第一个参数命名为props或者是一个解析赋值的参数样式,插件才能识别出来。 42 | 3. 如果你选择了ES6的代码风格,代码将生产在当前文件的最后一行。当然,如果你选择了ES7的代码风格, 代码将在你所选择的组件内部的第一行生成。 43 | 4. 如果自动生成的名字不是你期盼的那样,你可以在表格中双击名字进行修改,当然也可以手动添加一行或者删除你不需要的。 44 | 5. 如果你的组件中含有默认值的props, 插件会读取默认值的类型填充到最终表单之中。 45 | 6. 支持PropTypes.shape 和 defaultProps 生成。 46 | 7. 你可以在偏好设置中自定义你的代码风格。 47 | 8. 支持自动推断代码中的函数和数组类型,可以在设置中打开。 48 | 9. 现在已经支持了所有的类型 49 | 50 | ## 未来的计划 51 | 1. 开发VSCode版本的类似插件 52 | 2. 支持生成Flow风格或者TypeScript风格的类型检测,并且不使用PropTypes -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/ShapePropTypesModel.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import com.suming.plugin.bean.BasePropType; 4 | import com.suming.plugin.bean.PropTypeBean; 5 | import com.suming.plugin.utils.PropTypesHelper; 6 | 7 | import javax.swing.table.DefaultTableModel; 8 | import java.util.ArrayList; 9 | import java.util.Comparator; 10 | import java.util.List; 11 | import java.util.Vector; 12 | import java.util.stream.Collectors; 13 | 14 | public class ShapePropTypesModel extends DefaultTableModel { 15 | void addRow(BasePropType bean) { 16 | super.addRow(new Object[]{bean.name,bean.type,bean.isRequired}); 17 | } 18 | 19 | void initData(List beans){ 20 | String[] columnNames = { 21 | "name", 22 | "type", 23 | "isRequired", 24 | "ops"}; 25 | Object[][] data = new Object[beans.size()][4]; 26 | for (int i = 0; i < beans.size(); i++) { 27 | data[i][0] = beans.get(i).name; 28 | data[i][1] = beans.get(i).type; 29 | data[i][2] = beans.get(i).isRequired; 30 | data[i][3] = false; 31 | } 32 | this.setDataVector(data,columnNames); 33 | } 34 | 35 | void reInitData(List beans){ 36 | for (int i = 0; i < this.getRowCount(); i++) { 37 | this.removeRow(i); 38 | } 39 | for (BasePropType bean : beans) { 40 | this.addRow(bean); 41 | } 42 | } 43 | 44 | List data2PropList(){ 45 | Vector vector = this.getDataVector(); 46 | List propTypeBeans = new ArrayList<>(); 47 | for(Object a : vector){ 48 | if(a instanceof Vector){ 49 | Object[] o = ((Vector) a).toArray(); 50 | if(o[0].toString().trim().equals("")) continue; 51 | String name = o[0].toString(); 52 | String type = o[1].toString(); 53 | boolean isRequired = o[2].toString().equals("true"); 54 | BasePropType bean = new BasePropType(name, type ,isRequired); 55 | propTypeBeans.add(bean); 56 | } 57 | } 58 | // sort by name 59 | return propTypeBeans.stream() 60 | .filter(PropTypesHelper.distinctByKey(BasePropType::getName)) 61 | .sorted(Comparator.comparing(BasePropType::getName)) 62 | .collect(Collectors.toList()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/config/SettingEntry.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui.config; 2 | 3 | import com.intellij.openapi.components.ServiceManager; 4 | import com.intellij.openapi.options.Configurable; 5 | import com.intellij.openapi.options.ConfigurationException; 6 | import com.suming.plugin.bean.Setting; 7 | import com.suming.plugin.persist.SettingService; 8 | import org.jetbrains.annotations.Nls; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import javax.swing.*; 12 | 13 | public class SettingEntry implements Configurable{ 14 | private SettingService settingService = ServiceManager.getService(SettingService.class); 15 | private SettingForm form; 16 | 17 | @Nls 18 | @Override 19 | public String getDisplayName() { 20 | return "ReactPropTypes"; 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public JComponent createComponent() { 26 | SettingService settingService = ServiceManager.getService(SettingService.class); 27 | form = new SettingForm(settingService.getState()); 28 | return form.getRoot(); 29 | } 30 | 31 | @Override 32 | public boolean isModified() { 33 | Setting config = settingService.getState(); 34 | Setting setting = form !=null ? form.getNewSetting() : null; 35 | return setting !=null && (!config.getImportMode().equals(setting.getImportMode()) 36 | || !config.getEsVersion().equals(setting.getEsVersion()) 37 | || config.isSortProps() != setting.isSortProps() 38 | || config.getIndent() != setting.getIndent() 39 | || config.isNeedDefault() != setting.isNeedDefault() 40 | || config.isNoSemiColons() != setting.isNoSemiColons() 41 | || config.isInferByDestructure() != setting.isInferByDestructure() 42 | || config.isInferByDefaultProps() != setting.isInferByDefaultProps() 43 | || config.isInferByPropsCall() != setting.isInferByPropsCall() 44 | || config.isUncheckFunctionalComponent() != setting.isUncheckFunctionalComponent()); 45 | } 46 | 47 | @Override 48 | public void reset() { 49 | if(form != null){ 50 | form.updateSetting(settingService.getState()); 51 | } 52 | } 53 | 54 | @Override 55 | public void apply() throws ConfigurationException { 56 | settingService.loadState(form.getNewSetting()); 57 | } 58 | 59 | @Override 60 | public void disposeUIResources() { 61 | form = null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/config/SettingForm.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui.config; 2 | 3 | import com.suming.plugin.bean.ESVersion; 4 | import com.suming.plugin.bean.ImportMode; 5 | import com.suming.plugin.bean.Setting; 6 | 7 | import javax.swing.*; 8 | 9 | public class SettingForm { 10 | private JPanel root; 11 | private JComboBox importBox; 12 | private JComboBox esVersionBox; 13 | private JTextField indentInput; 14 | private JCheckBox noSemiColonsCheckBox; 15 | private JCheckBox defaultPropsCheckBox; 16 | private JCheckBox inferTypeByPropsDestructure; 17 | private JCheckBox inferTypeByDefaultProps; 18 | private JCheckBox inferTypeByPropsCall; 19 | private JCheckBox uncheckedFunctionalComponent; 20 | private JCheckBox sortPropTypes; 21 | 22 | JPanel getRoot() { 23 | return root; 24 | } 25 | 26 | SettingForm(Setting setting) { 27 | this.updateSetting(setting); 28 | } 29 | 30 | void updateSetting(Setting setting){ 31 | importBox.setSelectedItem(setting.getImportMode().getValue()); 32 | esVersionBox.setSelectedItem(setting.getEsVersion().toString()); 33 | indentInput.setText(setting.getIndent() + ""); 34 | noSemiColonsCheckBox.setSelected(setting.isNoSemiColons()); 35 | defaultPropsCheckBox.setSelected(setting.isNeedDefault()); 36 | inferTypeByPropsDestructure.setSelected(setting.isInferByDestructure()); 37 | inferTypeByDefaultProps.setSelected(setting.isInferByDefaultProps()); 38 | inferTypeByPropsCall.setSelected(setting.isInferByPropsCall()); 39 | uncheckedFunctionalComponent.setSelected(setting.isUncheckFunctionalComponent()); 40 | sortPropTypes.setSelected(setting.isSortProps()); 41 | } 42 | 43 | Setting getNewSetting(){ 44 | Setting setting = new Setting(); 45 | ImportMode importMode = importBox.getSelectedItem() == null ? 46 | ImportMode.Disabled : ImportMode.toEnum(importBox.getSelectedItem().toString()); 47 | ESVersion esVersion = ESVersion.valueOf(esVersionBox.getSelectedItem() !=null ? esVersionBox.getSelectedItem().toString() : "ES6"); 48 | setting.setImportMode(importMode); 49 | setting.setEsVersion(esVersion); 50 | setting.setIndent(Integer.parseInt(indentInput.getText())); 51 | setting.setNoSemiColons(noSemiColonsCheckBox.isSelected()); 52 | setting.setNeedDefault(defaultPropsCheckBox.isSelected()); 53 | setting.setInferByDestructure(inferTypeByPropsDestructure.isSelected()); 54 | setting.setInferByDefaultProps(inferTypeByDefaultProps.isSelected()); 55 | setting.setInferByPropsCall(inferTypeByPropsCall.isSelected()); 56 | setting.setUncheckFunctionalComponent(uncheckedFunctionalComponent.isSelected()); 57 | setting.setSortProps(sortPropTypes.isSelected()); 58 | return setting; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.suming.react.PropTypes 3 | ReactPropTypes 4 | 1.1.5 5 | 苏铭(Su Ming) 6 | 7 | 9 | 10 | Usage Instructions:
11 | 1. Select or take the cursor in your Component's name
12 | 2. Press command + N (Windows is alt + insert) show Generate Group and click PropTypesGenerate, or press 13 | shift + command + alt + P (Windows is shift + ctrl + alt + P) in the macOS
14 | 3. Edit the PropTypes Table to modify default type
15 | 16 | If you have any question , you can leave a issue in github.
17 | 18 | 使用介绍:
19 | 1. 选中你的组件名称或者将光标移动到组件的名称上 20 | 2. 按下command + N (Windows 是 alt + insert) 显示 Generate Group 并且点击 PropTypesGenerate, 或者直接按下快捷键 21 | shift + command + alt + P (Windows 是 shift + ctrl + alt + P) 在macOs系统中
22 | 3. 接下来就你可以在弹出的弹框中看到即将生成的PropType列表,并且你可以自由编辑它们。
23 | 24 | 如果你在使用过程中遇到了任何问题,或者想了解插件更多的使用方法,都可以在 github 25 | 中留言
26 | 27 | ]]>
28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 42 | 43 | 44 | com.intellij.modules.lang 45 | 46 | JavaScript 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
-------------------------------------------------------------------------------- /src/com/suming/plugin/bean/Setting.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.bean; 2 | 3 | public class Setting { 4 | 5 | // Generate mode 6 | private ESVersion esVersion = ESVersion.ES6; 7 | private ImportMode importMode = ImportMode.Disabled; 8 | private boolean sortProps = true; 9 | 10 | // Code Style 11 | private int indent = 2; 12 | private boolean noSemiColons = true; 13 | private boolean isNeedDefault = false; 14 | 15 | // Infer type 16 | private boolean inferByDestructure = true; 17 | private boolean inferByDefaultProps = true; 18 | private boolean inferByPropsCall = false; 19 | 20 | // Others 21 | private boolean uncheckFunctionalComponent = false; 22 | 23 | public Setting() { 24 | } 25 | 26 | public ESVersion getEsVersion() { 27 | return esVersion; 28 | } 29 | 30 | public void setEsVersion(ESVersion esVersion) { 31 | this.esVersion = esVersion; 32 | } 33 | 34 | public ImportMode getImportMode() { 35 | return importMode; 36 | } 37 | 38 | public void setImportMode(ImportMode importMode) { 39 | this.importMode = importMode; 40 | } 41 | 42 | public boolean isSortProps() { 43 | return sortProps; 44 | } 45 | 46 | public void setSortProps(boolean sortProps) { 47 | this.sortProps = sortProps; 48 | } 49 | 50 | public int getIndent() { 51 | return indent; 52 | } 53 | 54 | public void setIndent(int indent) { 55 | this.indent = indent; 56 | } 57 | 58 | public boolean isNeedDefault() { 59 | return isNeedDefault; 60 | } 61 | 62 | public void setNeedDefault(boolean needDefault) { 63 | isNeedDefault = needDefault; 64 | } 65 | 66 | public boolean isNoSemiColons() { 67 | return noSemiColons; 68 | } 69 | 70 | public void setNoSemiColons(boolean noSemiColons) { 71 | this.noSemiColons = noSemiColons; 72 | } 73 | 74 | public boolean isInferByDestructure() { 75 | return inferByDestructure; 76 | } 77 | 78 | public void setInferByDestructure(boolean inferByDestructure) { 79 | this.inferByDestructure = inferByDestructure; 80 | } 81 | 82 | public boolean isInferByDefaultProps() { 83 | return inferByDefaultProps; 84 | } 85 | 86 | public void setInferByDefaultProps(boolean inferByDefaultProps) { 87 | this.inferByDefaultProps = inferByDefaultProps; 88 | } 89 | 90 | public boolean isInferByPropsCall() { 91 | return inferByPropsCall; 92 | } 93 | 94 | public void setInferByPropsCall(boolean inferByPropsCall) { 95 | this.inferByPropsCall = inferByPropsCall; 96 | } 97 | 98 | public boolean isUncheckFunctionalComponent() { 99 | return uncheckFunctionalComponent; 100 | } 101 | 102 | public void setUncheckFunctionalComponent(boolean uncheckFunctionalComponent) { 103 | this.uncheckFunctionalComponent = uncheckFunctionalComponent; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/JsonInputDialog.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonParser; 6 | import com.suming.plugin.bean.BasePropType; 7 | import com.suming.plugin.utils.PropTypesHelper; 8 | 9 | import javax.swing.*; 10 | import java.awt.event.KeyEvent; 11 | import java.awt.event.WindowAdapter; 12 | import java.awt.event.WindowEvent; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public class JsonInputDialog extends JDialog { 18 | private JPanel contentPane; 19 | private JButton buttonOK; 20 | private JButton buttonCancel; 21 | private JTextArea input; 22 | private JLabel hintText; 23 | private JsonInputDialog.onSubmitListener onSubmitListener; 24 | 25 | public void setOnSubmitListener(JsonInputDialog.onSubmitListener onSubmitListener) { 26 | this.onSubmitListener = onSubmitListener; 27 | } 28 | 29 | public JsonInputDialog() { 30 | setContentPane(contentPane); 31 | setTitle("paste a json or a js Object"); 32 | setModal(true); 33 | getRootPane().setDefaultButton(buttonOK); 34 | 35 | buttonOK.addActionListener(e -> onOK()); 36 | 37 | buttonCancel.addActionListener(e -> onCancel()); 38 | 39 | // call onCancel() when cross is clicked 40 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 41 | addWindowListener(new WindowAdapter() { 42 | public void windowClosing(WindowEvent e) { 43 | onCancel(); 44 | } 45 | }); 46 | 47 | // call onCancel() on ESCAPE 48 | contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 49 | } 50 | 51 | private void onOK() { 52 | // add your code here 53 | String jsonStr = input.getText(); 54 | if( jsonStr != null && !jsonStr.trim().equals("")){ 55 | try{ 56 | List basePropTypes = new ArrayList<>(); 57 | JsonParser parser = new JsonParser(); 58 | JsonElement element = parser.parse(jsonStr); 59 | if(element instanceof JsonObject){ 60 | for (Object o : ((JsonObject) element).entrySet()) { 61 | Map.Entry entry = (Map.Entry) o; 62 | String name = entry.getKey().toString(); 63 | String value = entry.getValue().toString(); 64 | basePropTypes.add(new BasePropType(name, PropTypesHelper.getPropTypeByValue(value), false)); 65 | } 66 | if(this.onSubmitListener != null){ 67 | this.onSubmitListener.onSubmit(basePropTypes); 68 | } 69 | dispose(); 70 | }else { 71 | hintText.setText("Not a Json Object !"); 72 | } 73 | } catch (Exception e){ 74 | System.out.println(e); 75 | hintText.setText("Incorrectly formatting !"); 76 | } 77 | }else { 78 | hintText.setText("Can not be empty !"); 79 | } 80 | } 81 | 82 | private void onCancel() { 83 | // add your code here if necessary 84 | dispose(); 85 | } 86 | public interface onSubmitListener{ 87 | void onSubmit(List beans); 88 | } 89 | 90 | 91 | public static void main(String[] args) { 92 | JsonInputDialog dialog = new JsonInputDialog(); 93 | dialog.pack(); 94 | dialog.setVisible(true); 95 | System.exit(0); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReactPropTypes-Plugin 2 | 中文文档 3 | 4 | This is a JetBrains plug-in that automatically generates PropTypes code for React components, and only supports ES6 later at the moment. If you need to support ES5, please leave a message in issue.Compatible with: IntelliJ IDEA, PhpStorm, WebStorm, PyCharm, RubyMine, AppCode, CLion, Gogland, Rider. 5 | 6 | If you are want use it by Visual Code or command line, I suggest you to use another plugin: [react-proptypes-generate](https://github.com/dpzxsm/react-proptypes-generate), now it is imperfect, welcome issue and PR. 7 | 8 | ## Installed 9 | 1. In plugin store search "ReactPropTypes" and install it , this is Store Link, Welcome comments. 10 | 2. Click ReactPropTypes.jar(Recently, but may Unstable) to download and open Setting/Plugins/Install Plugin from disk to install. 11 | 12 | ## How to use 13 | 1. Select your Component's name 14 | 2. Press command + N (Windows is alt + insert) show Generate Group and select PropTypesGenerate, or press shift + command + alt + P (Windows is shift + ctrl + alt + P) in the macOS 15 | 3. Edit the PropTypes Table to modify default type 16 | 17 | ## Preview 18 | #### Class Components 19 | ![img](./ScreenShots/ScreenShot1.gif) 20 | #### Functional Components 21 | ![img](./ScreenShots/ScreenShot2.gif) 22 | #### When you select `Shape` as the PropType 23 | ![img](./ScreenShots/ScreenShot3.gif) 24 | 25 | ## How to Build and Development the project 26 | 27 | #### Dependency Library 28 | * IntelliJ Platform Plugin SDK 29 | * JavaScriptLanguage's lib (In macOS, the path is /Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/lib, Windows are similar) 30 | 31 | #### Development Step 32 | 1. Download the latest IntelliJ IDEA Ultimate 33 | 2. Clone and Open this project 34 | 3. Delete default module and import plugin module by `ReactPropTypes.iml` 35 | 4. Config Project SDK named IntelliJ IDEA IU-182.** and Config language level 8 36 | 5. Config plugin out dir and plugin launch Configurations 37 | 38 | #### For Example 39 | ![img](./ScreenShots/ScreenShot4.gif) 40 | 41 | ## Features 42 | 1. Get a heightLight text as component's name if you are not select any text. 43 | 1. In the Standard ES6 component, the plugin can distinguish props's reference and destructuring assignment with keyword "props" or "nextProps". 44 | 2. In the Stateless component, only when your first param must be named "props" or a destructuring parameter the plugin can distinguished. 45 | 3. If you select ES6 code style , the propTypes code will generate at the last line .Of cause, if you select ES7 code style, the propTypes code will generate at the component inside's first line. 46 | 4. Double Click the row's name in the table, can modify distinguished name if not you expect, also support add a new row or delete what you not need. 47 | 5. If your component has a default value for props, the plugin will fill the default type to the table. 48 | 6. Support PropTypes.shape and handle defaultProps. 49 | 7. You can custom your code generate's style. 50 | 8. Supports automatic inference of functions and array types in code, and can be opened in the settings. 51 | 9. Support full list of PropTypes 52 | 53 | 54 | ## Next plan 55 | 1. Developing a similar plugin on the VS Code's platform 56 | 2. Support Flow Or TypeScript's TypeChecker, and not use PropTypes -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/PropTypesModel.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import com.intellij.openapi.components.ServiceManager; 4 | import com.suming.plugin.bean.BasePropType; 5 | import com.suming.plugin.bean.PropTypeBean; 6 | import com.suming.plugin.persist.SettingService; 7 | import com.suming.plugin.utils.PropTypesHelper; 8 | 9 | import javax.swing.table.DefaultTableModel; 10 | import java.util.*; 11 | import java.util.stream.Collectors; 12 | 13 | class PropTypesModel extends DefaultTableModel { 14 | SettingService settingService = ServiceManager.getService(SettingService.class); 15 | 16 | void addRow(PropTypeBean bean) { 17 | HashMap extraData = new HashMap<>(); 18 | super.addRow(new Object[]{bean.name, bean.type, bean.isRequired, 19 | bean.getDefaultValue(), extraData}); 20 | } 21 | 22 | void initData(List beans) { 23 | String[] columnNames = { 24 | "name", 25 | "type", 26 | "isRequired", 27 | "defaultValue", 28 | "ops"}; 29 | Object[][] data = new Object[beans.size()][5]; 30 | for (int i = 0; i < beans.size(); i++) { 31 | data[i][0] = beans.get(i).name; 32 | data[i][1] = beans.get(i).type; 33 | data[i][2] = beans.get(i).isRequired; 34 | data[i][3] = beans.get(i).getDefaultValue(); 35 | HashMap extraData = new HashMap<>(); 36 | extraData.put("shapeProps", beans.get(i).getShapePropTypeList()); 37 | extraData.put("jsonData", beans.get(i).getJsonData()); 38 | data[i][4] = extraData; 39 | } 40 | this.setDataVector(data, columnNames); 41 | } 42 | 43 | @SuppressWarnings("unchecked") 44 | void updateExtraDataByName(int row, String name, Object data) { 45 | HashMap extraData = (HashMap) this.getValueAt(row, 4); 46 | extraData.put(name, data); 47 | this.setValueAt(extraData, row, 4); 48 | } 49 | 50 | String getValueFromIndex(int column, int row) { 51 | Object[] objects = getDataVector().toArray(); 52 | if (row <= objects.length - 1) { 53 | Vector rowVector = ((Vector) objects[row]); 54 | return rowVector.elementAt(column).toString(); 55 | } else { 56 | return ""; 57 | } 58 | } 59 | 60 | List data2PropList() { 61 | Vector vector = this.getDataVector(); 62 | List propTypeBeans = new ArrayList<>(); 63 | for (Object a : vector) { 64 | if (a instanceof Vector) { 65 | Object[] o = ((Vector) a).toArray(); 66 | if (o[0].toString().trim().equals("")) continue; 67 | String name = o[0].toString(); 68 | String type = o[1].toString(); 69 | boolean isRequired = o[2].toString().equals("true"); 70 | String defaultValue = o[3] == null ? null : o[3].toString(); 71 | HashMap extraData = (HashMap) o[4]; 72 | Object shapePropsObj = extraData.get("shapeProps"); 73 | PropTypeBean bean = new PropTypeBean(name, type, isRequired, defaultValue); 74 | if (type != null && (type.equals("shape") || type.equals("exact")) && shapePropsObj != null) { 75 | List shapePropList = ((List) shapePropsObj).stream() 76 | .map(e -> (BasePropType) e).collect(Collectors.toList()); 77 | bean.setShapePropTypeList(shapePropList); 78 | } 79 | String jsonData = (String) extraData.get("jsonData"); 80 | if (type != null && jsonData != null && !jsonData.equals("")) { 81 | bean.setJsonData(jsonData); 82 | } 83 | propTypeBeans.add(bean); 84 | } 85 | } 86 | // sort by name 87 | if (this.settingService.getState().isSortProps()) { 88 | return propTypeBeans.stream() 89 | .filter(PropTypesHelper.distinctByKey(PropTypeBean::getName)) 90 | .sorted(Comparator.comparing(PropTypeBean::getName)) 91 | .collect(Collectors.toList()); 92 | } else { 93 | return propTypeBeans.stream() 94 | .filter(PropTypesHelper.distinctByKey(PropTypeBean::getName)) 95 | .collect(Collectors.toList()); 96 | } 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/JsonInputDialog.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 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/ShapePropTypesDialog.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 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/ShapePropTypesDialog.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import com.intellij.ui.table.JBTable; 4 | import com.suming.plugin.bean.BasePropType; 5 | import sun.swing.table.DefaultTableCellHeaderRenderer; 6 | 7 | import javax.swing.*; 8 | import javax.swing.table.TableColumn; 9 | import java.awt.*; 10 | import java.awt.event.KeyEvent; 11 | import java.awt.event.WindowAdapter; 12 | import java.awt.event.WindowEvent; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import static javax.swing.ListSelectionModel.SINGLE_SELECTION; 17 | 18 | public class ShapePropTypesDialog extends JDialog { 19 | private JPanel contentPane; 20 | private JButton buttonOK; 21 | private JButton buttonCancel; 22 | private JButton pasteWithJSONButton; 23 | private JScrollPane sp; 24 | private JButton addPropBtn; 25 | private JTable table; 26 | private ShapePropTypesModel model; 27 | private ShapePropTypesDialog.onSubmitListener onSubmitListener; 28 | 29 | public void setOnSubmitListener(ShapePropTypesDialog.onSubmitListener onSubmitListener) { 30 | this.onSubmitListener = onSubmitListener; 31 | } 32 | 33 | public ShapePropTypesDialog(List propTypeList, String title) { 34 | setContentPane(contentPane); 35 | setTitle((title !=null && !title.equals("")? title : "unnamed")); 36 | setModal(true); 37 | getRootPane().setDefaultButton(buttonOK); 38 | 39 | buttonOK.addActionListener(e -> onOK()); 40 | 41 | buttonCancel.addActionListener(e -> onCancel()); 42 | 43 | // call onCancel() when cross is clicked 44 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 45 | addWindowListener(new WindowAdapter() { 46 | public void windowClosing(WindowEvent e) { 47 | onCancel(); 48 | } 49 | }); 50 | 51 | // call onCancel() on ESCAPE 52 | contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 53 | 54 | 55 | // init Table 56 | initTable(propTypeList); 57 | // init other widget 58 | initOtherWidgets(); 59 | } 60 | 61 | private void initTable(List propTypeList) { 62 | table = new JBTable(); 63 | model = new ShapePropTypesModel(); 64 | model.initData(propTypeList); 65 | table.setModel(model); 66 | final DefaultListSelectionModel defaultListSelectionModel = new DefaultListSelectionModel(); 67 | defaultListSelectionModel.setSelectionMode(SINGLE_SELECTION); 68 | table.setSelectionModel(defaultListSelectionModel); 69 | // form header center 70 | DefaultTableCellHeaderRenderer thr = new DefaultTableCellHeaderRenderer(); 71 | thr.setHorizontalAlignment(JLabel.CENTER); 72 | table.getTableHeader().setDefaultRenderer(thr); 73 | //render special column 74 | TableColumn nameColumn = table.getColumn("name"); 75 | TableColumn typeColumn = table.getColumn("type"); 76 | TableColumn isRequireColumn = table.getColumn("isRequired"); 77 | TableColumn operationColumn = table.getColumn("ops"); 78 | nameColumn.setCellRenderer(new NameTextRenderer(true, "Please input name !")); 79 | nameColumn.setCellEditor(new DefaultCellEditor(new NameTextRenderer(false, "Please input name !"))); 80 | nameColumn.setPreferredWidth(200); 81 | typeColumn.setCellEditor(new DefaultCellEditor(new ComboBoxRenderer(false))); 82 | typeColumn.setCellRenderer(new ComboBoxRenderer(false)); 83 | typeColumn.setPreferredWidth(100); 84 | isRequireColumn.setCellEditor(new DefaultCellEditor(new CheckBoxRenderer())); 85 | isRequireColumn.setCellRenderer(new CheckBoxRenderer()); 86 | isRequireColumn.setWidth(50); 87 | ButtonEditor buttonEditor = new ButtonEditor(); 88 | operationColumn.setCellRenderer(new ButtonRenderer()); 89 | operationColumn.setCellEditor(buttonEditor); 90 | operationColumn.setPreferredWidth(80); 91 | sp.setViewportView(table); 92 | } 93 | 94 | private void initOtherWidgets(){ 95 | // add button onClick event 96 | addPropBtn.addActionListener(e -> { 97 | model.addRow(new BasePropType("","any", false )); 98 | int rowCount = table.getRowCount(); 99 | table.getSelectionModel().setSelectionInterval(rowCount-1 , rowCount- 1 ); 100 | Rectangle rect = table.getCellRect(rowCount-1 , 0 , true ); 101 | table.scrollRectToVisible(rect); 102 | }); 103 | pasteWithJSONButton.addActionListener(e -> { 104 | JsonInputDialog dialog = new JsonInputDialog(); 105 | dialog.pack(); 106 | dialog.setLocationRelativeTo(this); 107 | dialog.setOnSubmitListener(beans -> model.reInitData(beans)); 108 | dialog.setVisible(true); 109 | }); 110 | } 111 | 112 | private void onOK() { 113 | // add your code here 114 | dispose(); 115 | if(this.onSubmitListener!=null){ 116 | this.onSubmitListener.onSubmit(model.data2PropList()); 117 | } 118 | } 119 | 120 | private void onCancel() { 121 | dispose(); 122 | } 123 | 124 | public interface onSubmitListener{ 125 | void onSubmit(List beans); 126 | } 127 | 128 | public static void main(String[] args) { 129 | List list = new ArrayList<>(); 130 | list.add(new BasePropType("test", "string", true)); 131 | ShapePropTypesDialog dialog = new ShapePropTypesDialog(list, "测试弹框"); 132 | dialog.pack(); 133 | dialog.setVisible(true); 134 | System.exit(0); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/com/suming/plugin/utils/PropTypesHelper.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.utils; 2 | 3 | import com.suming.plugin.bean.BasePropType; 4 | import com.suming.plugin.bean.PropTypeBean; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.Set; 10 | import java.util.concurrent.ConcurrentHashMap; 11 | import java.util.function.Function; 12 | import java.util.function.Predicate; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | public class PropTypesHelper { 17 | public static Predicate distinctByKey(Function keyExtractor) { 18 | Set seen = ConcurrentHashMap.newKeySet(); 19 | return t -> seen.add(keyExtractor.apply(t)); 20 | } 21 | 22 | public static Comparator sortByKey(Set reverseSet, Function... keyExtractors) { 23 | return (o1, o2) -> { 24 | for (int i = 0; i < keyExtractors.length; i++) { 25 | Function keyExtractor = keyExtractors[i]; 26 | String value1 = keyExtractor.apply(o1) == null ? "" : keyExtractor.apply(o1).toString(); 27 | String value2 = keyExtractor.apply(o2) == null ? "" : keyExtractor.apply(o2).toString(); 28 | int result = reverseSet.contains(i) ? value2.compareTo(value1) : value1.compareTo(value2); 29 | if (result != 0) { 30 | return result; 31 | } 32 | } 33 | return 0; 34 | }; 35 | } 36 | 37 | public static boolean isBool(String str) { 38 | Pattern pattern = Pattern.compile("true|false"); 39 | Matcher isNum = pattern.matcher(str); 40 | if (!isNum.matches()) { 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | public static boolean isNumeric(String str) { 47 | Pattern pattern = Pattern.compile("-?[0-9]+.?[0-9]*"); 48 | Matcher isNum = pattern.matcher(str); 49 | if (!isNum.matches()) { 50 | return false; 51 | } 52 | return true; 53 | } 54 | 55 | public static String getPropTypeByValue(String value) { 56 | String type = "any"; 57 | if (value.startsWith("\"") && value.endsWith("\"")) { 58 | type = "string"; 59 | } else if (value.startsWith("\'") && value.endsWith("\'")) { 60 | type = "string"; 61 | } else if (value.startsWith("{") && value.endsWith("}")) { 62 | type = "object"; 63 | } else if (value.startsWith("[") && value.endsWith("]")) { 64 | type = "array"; 65 | } else if (PropTypesHelper.isBool(value)) { 66 | type = "bool"; 67 | } else if (PropTypesHelper.isNumeric(value)) { 68 | type = "number"; 69 | } 70 | return type; 71 | } 72 | 73 | public static void updatePropTypeFromCode(PropTypeBean bean, String codeStr) { 74 | // 去除换行符 75 | codeStr = codeStr.replaceAll("\n", ""); 76 | Pattern p = Pattern.compile("(React)?\\s*\\.?\\s*PropTypes\\s*\\." + 77 | "\\s*(any|string|object|bool|func|number|array|symbol|node|exact|element|arrayOf|objectOf|oneOf|instanceOf|oneOfType|shape)" + 78 | "\\s*(\\((.*)\\))?" + 79 | "\\s*\\.?\\s*(isRequired)?"); 80 | Matcher m = p.matcher(codeStr); 81 | if (m.matches()) { 82 | bean.setType(m.group(2) == null ? "any" : m.group(2)); 83 | if (m.group(4) != null) { 84 | Pattern p2 = Pattern.compile("\\s*\\{(.*)}\\s*"); 85 | Matcher m2 = p2.matcher(m.group(4)); 86 | if (m2.matches()) { 87 | bean.setShapePropTypeList(matchShapePropType(m2.group(1))); 88 | } else { 89 | bean.setJsonData(m.group(4)); 90 | } 91 | } 92 | bean.setRequired(m.group(5) != null); 93 | } 94 | } 95 | 96 | private static List matchShapePropType(String codeStr) { 97 | if (codeStr == null) return null; 98 | List shapePropList = new ArrayList<>(); 99 | String[] propsCode = codeStr.trim().split(","); 100 | for (String childCode : propsCode) { 101 | Pattern p = Pattern.compile("\\s*(.*):\\s*" + "(React)?\\s*\\.?\\s*PropTypes\\s*\\." + 102 | "\\s*(any|string|object|bool|func|number|array|symbol|node|exact|element|arrayOf|objectOf|oneOf|instanceOf|oneOfType)" + 103 | "\\s*(\\((.*)\\))?" + 104 | "\\s*\\.?\\s*(isRequired)?"); 105 | Matcher m = p.matcher(childCode); 106 | if (m.matches() && m.group(1) != null) { 107 | BasePropType bean = new BasePropType(m.group(1), m.group(3) == null ? "any" : m.group(3), 108 | m.group(6) != null); 109 | if(m.group(5) != null){ 110 | bean.setJsonData(m.group(5)); 111 | } 112 | shapePropList.add(bean); 113 | } 114 | } 115 | return shapePropList; 116 | } 117 | 118 | public static String getBlank(int count) { 119 | StringBuilder st = new StringBuilder(); 120 | if (count < 0) { 121 | count = 0; 122 | } 123 | for (int i = 0; i < count; i++) { 124 | st.append(" "); 125 | } 126 | return st.toString(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/PropTypesDialog.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 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
135 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/config/SettingForm.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 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 |
169 | -------------------------------------------------------------------------------- /src/com/suming/plugin/ui/PropTypesDialog.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin.ui; 2 | 3 | import com.intellij.openapi.components.ServiceManager; 4 | import com.intellij.ui.table.JBTable; 5 | import com.suming.plugin.bean.*; 6 | import com.suming.plugin.bean.Component; 7 | import com.suming.plugin.persist.SettingService; 8 | import sun.swing.table.DefaultTableCellHeaderRenderer; 9 | 10 | import javax.swing.*; 11 | import javax.swing.event.TableModelListener; 12 | import javax.swing.table.TableColumn; 13 | import java.awt.*; 14 | import java.awt.event.KeyEvent; 15 | import java.awt.event.WindowAdapter; 16 | import java.awt.event.WindowEvent; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.stream.Collectors; 21 | 22 | import static javax.swing.ListSelectionModel.SINGLE_SELECTION; 23 | 24 | public class PropTypesDialog extends JDialog { 25 | private JPanel contentPane; 26 | private JButton buttonOK; 27 | private JButton buttonCancel; 28 | private JScrollPane sp; 29 | private JComboBox esVersionBox; 30 | private JButton addPropBtn; 31 | private JComboBox importBox; 32 | private JCheckBox handleDefaultPropCheckBox; 33 | private JTable table; 34 | private PropTypesModel model; 35 | private onSubmitListener onSubmitListener; 36 | private SettingService settingService = ServiceManager.getService(SettingService.class); 37 | 38 | public void setOnSubmitListener(PropTypesDialog.onSubmitListener onSubmitListener) { 39 | this.onSubmitListener = onSubmitListener; 40 | } 41 | 42 | public PropTypesDialog(List propTypeList, Component component) { 43 | setContentPane(contentPane); 44 | setModal(true); 45 | getRootPane().setDefaultButton(buttonOK); 46 | 47 | buttonOK.addActionListener(e -> onOK()); 48 | 49 | buttonCancel.addActionListener(e -> onCancel()); 50 | 51 | // call onCancel() when cross is clicked 52 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 53 | addWindowListener(new WindowAdapter() { 54 | public void windowClosing(WindowEvent e) { 55 | onCancel(); 56 | } 57 | }); 58 | 59 | // call onCancel() on ESCAPE 60 | contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), 61 | JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 62 | // init Table 63 | initTable(propTypeList); 64 | // init other widget 65 | initOtherWidgets(component); 66 | } 67 | 68 | private void initTable(List propTypeList) { 69 | table = new JBTable(); 70 | model = new PropTypesModel(); 71 | model.addTableModelListener(e -> { 72 | //column = 1 表示是选中的type 73 | int column = e.getColumn(); 74 | if (column == 1 && e.getFirstRow() == e.getLastRow()) { 75 | int row = e.getFirstRow(); 76 | String type = model.getValueFromIndex(e.getColumn(), e.getFirstRow()); 77 | if (type.equals("shape") || type.equals("exact")) { 78 | String propName = (String) model.getValueAt(row, 0); 79 | HashMap extraData = (HashMap) model.getValueAt(row, 4); 80 | Object shapePropsObj = extraData.get("shapeProps"); 81 | List shapePropList = shapePropsObj != null ? ((List) shapePropsObj).stream() 82 | .map(o -> (BasePropType) o).collect(Collectors.toList()) : new ArrayList<>(); 83 | ShapePropTypesDialog dialog = new ShapePropTypesDialog(shapePropList, propName); 84 | dialog.pack(); 85 | dialog.setLocationRelativeTo(this); 86 | dialog.setOnSubmitListener(beans -> model.updateExtraDataByName(row, "shapeProps", beans)); 87 | dialog.setVisible(true); 88 | } 89 | } 90 | }); 91 | model.initData(propTypeList); 92 | table.setModel(model); 93 | final DefaultListSelectionModel defaultListSelectionModel = new DefaultListSelectionModel(); 94 | defaultListSelectionModel.setSelectionMode(SINGLE_SELECTION); 95 | table.setSelectionModel(defaultListSelectionModel); 96 | // form header center 97 | DefaultTableCellHeaderRenderer thr = new DefaultTableCellHeaderRenderer(); 98 | thr.setHorizontalAlignment(JLabel.CENTER); 99 | table.getTableHeader().setDefaultRenderer(thr); 100 | //render special column 101 | TableColumn nameColumn = table.getColumn("name"); 102 | TableColumn typeColumn = table.getColumn("type"); 103 | TableColumn isRequireColumn = table.getColumn("isRequired"); 104 | TableColumn infoColumn = table.getColumn("defaultValue"); 105 | TableColumn operationColumn = table.getColumn("ops"); 106 | nameColumn.setCellRenderer(new NameTextRenderer(true, "Please input name !")); 107 | nameColumn.setCellEditor(new DefaultCellEditor(new NameTextRenderer(false, "Please input name !"))); 108 | typeColumn.setCellEditor(new DefaultCellEditor(new ComboBoxRenderer(true))); 109 | typeColumn.setCellRenderer(new ComboBoxRenderer(true)); 110 | typeColumn.setMinWidth(90); 111 | typeColumn.setMaxWidth(150); 112 | isRequireColumn.setCellEditor(new DefaultCellEditor(new CheckBoxRenderer())); 113 | isRequireColumn.setCellRenderer(new CheckBoxRenderer()); 114 | isRequireColumn.setMaxWidth(100); 115 | infoColumn.setCellRenderer(new NameTextRenderer(true, "")); 116 | infoColumn.setCellEditor(new DefaultCellEditor(new NameTextRenderer(false, ""))); 117 | ButtonEditor buttonEditor = new ButtonEditor(); 118 | operationColumn.setCellRenderer(new ButtonRenderer()); 119 | operationColumn.setCellEditor(buttonEditor); 120 | operationColumn.setMinWidth(100); 121 | operationColumn.setMaxWidth(100); 122 | sp.setViewportView(table); 123 | } 124 | 125 | private void initOtherWidgets(Component component) { 126 | Setting setting = settingService.getState(); 127 | if (component.getEsVersion() != null) { 128 | this.esVersionBox.setSelectedItem(component.getEsVersion().toString()); 129 | } else if (setting.getEsVersion() != null) { 130 | this.esVersionBox.setSelectedItem(setting.getEsVersion().toString()); 131 | } 132 | // if the component is a stateless component, disable esVersionBox 133 | if (component.getComponentType() == ComponentType.STATELESS) { 134 | this.esVersionBox.setEnabled(false); 135 | } 136 | if (setting.getImportMode() != null) { 137 | this.importBox.setSelectedItem(setting.getImportMode().getValue()); 138 | } 139 | this.handleDefaultPropCheckBox.setSelected(setting.isNeedDefault()); 140 | 141 | this.esVersionBox.addActionListener(e -> { 142 | // current hasn't propTypes 143 | if (component.getEsVersion() == null) { 144 | Object selectItem = esVersionBox.getSelectedItem(); 145 | ESVersion esVersion = ESVersion.valueOf(selectItem != null ? selectItem.toString() : "ES6"); 146 | setting.setEsVersion(esVersion); 147 | } 148 | }); 149 | this.importBox.addActionListener(e -> { 150 | Object selectImportItem = importBox.getSelectedItem(); 151 | ImportMode importMode = selectImportItem == null ? ImportMode.Disabled : ImportMode.toEnum(selectImportItem.toString()); 152 | setting.setImportMode(importMode); 153 | }); 154 | // add button onClick event 155 | addPropBtn.addActionListener(e -> { 156 | PropTypeBean bean = new PropTypeBean(""); 157 | model.addRow(bean); 158 | int rowCount = table.getRowCount(); 159 | table.getSelectionModel().setSelectionInterval(rowCount - 1, rowCount - 1); 160 | Rectangle rect = table.getCellRect(rowCount - 1, 0, true); 161 | table.scrollRectToVisible(rect); 162 | }); 163 | handleDefaultPropCheckBox.addActionListener(e -> { 164 | setting.setNeedDefault(handleDefaultPropCheckBox.isSelected()); 165 | }); 166 | } 167 | 168 | private void onOK() { 169 | for (TableModelListener listener : model.getTableModelListeners()) { 170 | model.removeTableModelListener(listener); 171 | } 172 | dispose(); 173 | if (this.onSubmitListener != null) { 174 | Object selectItem = esVersionBox.getSelectedItem(); 175 | ESVersion esVersion = selectItem == null ? ESVersion.ES6 : ESVersion.valueOf(selectItem.toString()); 176 | ImportMode importMode = settingService.getState().getImportMode(); 177 | this.onSubmitListener.onSubmit(model.data2PropList(), importMode, esVersion, handleDefaultPropCheckBox.isSelected()); 178 | } 179 | } 180 | 181 | private void onCancel() { 182 | for (TableModelListener listener : model.getTableModelListeners()) { 183 | model.removeTableModelListener(listener); 184 | } 185 | dispose(); 186 | } 187 | 188 | public interface onSubmitListener { 189 | void onSubmit(List beans, ImportMode importMode, ESVersion esVersion, boolean handleDefault); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/com/suming/plugin/PropTypeAction.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin; 2 | 3 | import com.intellij.lang.ecmascript6.psi.ES6Class; 4 | import com.intellij.lang.ecmascript6.psi.ES6ImportDeclaration; 5 | import com.intellij.openapi.editor.Document; 6 | import com.intellij.openapi.editor.Editor; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.util.TextRange; 9 | import com.intellij.openapi.wm.WindowManager; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.PsiFileFactory; 13 | import com.intellij.psi.PsiWhiteSpace; 14 | import com.suming.plugin.bean.*; 15 | import com.suming.plugin.constants.SpecialPropTypes; 16 | import com.suming.plugin.ui.PropTypesDialog; 17 | import com.suming.plugin.utils.PropTypesHelper; 18 | import com.suming.plugin.utils.PsiElementHelper; 19 | 20 | import java.util.Arrays; 21 | import java.util.List; 22 | import java.util.stream.Collectors; 23 | 24 | public class PropTypeAction extends CommonAction { 25 | 26 | @Override 27 | void actionPerformed(Project project, 28 | Editor editor, 29 | PsiFile file, 30 | String selectedText, 31 | List propNameList, 32 | Component component) { 33 | PropTypesDialog dialog = new PropTypesDialog(propNameList, component); 34 | dialog.pack(); 35 | dialog.setLocationRelativeTo(WindowManager.getInstance().getFrame(project)); 36 | dialog.setOnSubmitListener((beans, importMode, esVersion, handleDefault) -> { 37 | Document document = editor.getDocument(); 38 | 39 | runCommand(project, () -> { 40 | //insert PropTypes Object 41 | insertPropTypesCodeString(document, file, selectedText, beans, esVersion); 42 | //insert import statement 43 | autoInsertImportPropTypes(document, file, importMode); 44 | //insert defaultProps Object 45 | if (handleDefault) { 46 | List defaultBeans = beans.stream() 47 | .filter(o -> o.getDefaultValue() != null && !o.getDefaultValue().trim().equals("")) 48 | .collect(Collectors.toList()); 49 | if (defaultBeans.size() > 0) { 50 | PsiFile nextFile = PsiFileFactory.getInstance(project).createFileFromText(file.getLanguage(), document.getText()); 51 | insertDefaultPropsCodeString(document, nextFile, selectedText, defaultBeans, esVersion); 52 | } 53 | } 54 | }); 55 | }); 56 | dialog.setVisible(true); 57 | } 58 | 59 | 60 | private void autoInsertImportPropTypes(Document document, PsiFile file, ImportMode importMode) { 61 | 62 | if (importMode == ImportMode.Disabled) return; 63 | boolean isNew = importMode == ImportMode.NewModules; 64 | 65 | if (!hasImportPropTypes(file)) { 66 | int firstImportIndex = findFirstImportIndex(file); 67 | if (isNew) { 68 | document.insertString(firstImportIndex, "import PropTypes from \'prop-types\'\n"); 69 | } else { 70 | ES6ImportDeclaration reactImport = getReactImportDeclaration(file); 71 | if (reactImport == null) { 72 | document.insertString(firstImportIndex, "import React, {PropTypes} from \'react\'\n"); 73 | } else { 74 | if (reactImport.getFromClause() == null) return; 75 | PsiElement pFrom = reactImport.getFromClause(); 76 | if (pFrom.getPrevSibling() == null || pFrom.getPrevSibling().getPrevSibling() == null) return; 77 | PsiElement p1 = pFrom.getPrevSibling(); 78 | PsiElement p2 = p1.getPrevSibling(); 79 | PsiElement rbrace = null; 80 | if (p1.getText().equals("}")) { 81 | rbrace = p1; 82 | } else if (p2.getText().equals("}")) { 83 | rbrace = p2; 84 | } 85 | if (rbrace != null) { 86 | int index = rbrace.getTextRange().getStartOffset(); 87 | boolean isNeedComma = reactImport.getImportSpecifiers().length > 0; 88 | document.insertString(index, isNeedComma ? ",PropTypes" : "PropTypes"); 89 | } else { 90 | int index = pFrom.getTextRange().getStartOffset(); 91 | PsiElement p = PsiElementHelper.getRealPreElement(pFrom); 92 | boolean isNeedComma = p != null && !p.getText().equals(","); 93 | document.insertString(index, isNeedComma ? ",{ PropTypes } " : "{ PropTypes }"); 94 | } 95 | } 96 | 97 | } 98 | } 99 | } 100 | 101 | private void insertDefaultPropsCodeString(Document document, PsiFile file, String componentName, 102 | List beans, ESVersion esVersion) { 103 | PsiElement es7Element = getES7FieldElementByName(file, componentName, "defaultProps"); 104 | PsiElement es6Element = getES6FieldByName(file, componentName, "defaultProps"); 105 | boolean isES7 = esVersion == ESVersion.ES7; 106 | if (isES7 && es7Element == null) { 107 | ES6Class es6Class = getSelectES6Component(componentName, file); 108 | if (es6Class != null) { 109 | PsiElement p = PsiElementHelper.getRealFirstChild(es6Class); 110 | if (p != null) { 111 | TextRange pRange = p.getTextRange(); 112 | document.insertString(pRange.getStartOffset(), 113 | getInsertDefaultPropsString(componentName, beans, 114 | true, true) + "\n\n "); 115 | } 116 | } 117 | } else if (!isES7 && es6Element == null) { 118 | PsiElement p = file.getLastChild(); 119 | TextRange pRange = p.getTextRange(); 120 | if (p instanceof PsiWhiteSpace) { 121 | document.replaceString(pRange.getStartOffset(), pRange.getEndOffset(), 122 | getInsertDefaultPropsString(componentName, beans, 123 | true, false)); 124 | } else { 125 | document.insertString(pRange.getEndOffset(), 126 | getInsertDefaultPropsString(componentName, beans, 127 | true, false)); 128 | } 129 | } else { 130 | TextRange textRange = (isES7 ? es7Element : es6Element).getParent().getTextRange(); 131 | document.replaceString(textRange.getStartOffset(), textRange.getEndOffset(), 132 | getInsertDefaultPropsString(componentName, beans, false, isES7)); 133 | } 134 | } 135 | 136 | private void insertPropTypesCodeString(Document document, PsiFile file, String componentName, 137 | List beans, ESVersion esVersion) { 138 | PsiElement es7Element = getES7FieldElementByName(file, componentName, "propTypes"); 139 | PsiElement es6Element = getES6FieldByName(file, componentName, "propTypes"); 140 | boolean isES7 = esVersion == ESVersion.ES7; 141 | if (isES7 && es7Element == null) { 142 | ES6Class es6Class = getSelectES6Component(componentName, file); 143 | if (es6Class != null) { 144 | PsiElement p = PsiElementHelper.getRealFirstChild(es6Class); 145 | if (p != null) { 146 | TextRange pRange = p.getTextRange(); 147 | document.insertString(pRange.getStartOffset(), 148 | getInsertPropTypeCodeString(componentName, beans, 149 | true, true) + "\n\n "); 150 | } 151 | } 152 | } else if (!isES7 && es6Element == null) { 153 | PsiElement p = file.getLastChild(); 154 | TextRange pRange = p.getTextRange(); 155 | if (p instanceof PsiWhiteSpace) { 156 | document.replaceString(pRange.getStartOffset(), pRange.getEndOffset(), 157 | getInsertPropTypeCodeString(componentName, beans, 158 | true, false)); 159 | } else { 160 | document.insertString(pRange.getEndOffset(), 161 | getInsertPropTypeCodeString(componentName, beans, 162 | true, false)); 163 | } 164 | } else { 165 | TextRange textRange = (isES7 ? es7Element : es6Element).getParent().getTextRange(); 166 | document.replaceString(textRange.getStartOffset(), textRange.getEndOffset(), 167 | getInsertPropTypeCodeString(componentName, beans, false, isES7)); 168 | } 169 | } 170 | 171 | private String getInsertPropTypeCodeString(String componentName, List beans, 172 | boolean isNewPropTypes, boolean isES7) { 173 | int indent = settingService.getState().getIndent(); 174 | boolean noSemiColons = settingService.getState().isNoSemiColons(); 175 | StringBuilder sb = new StringBuilder(); 176 | String propsObjBlank = isES7 ? " " : ""; 177 | String propsBlank = propsObjBlank + PropTypesHelper.getBlank(indent); 178 | if (isES7) { 179 | sb.append("static propTypes = {\n"); 180 | } else { 181 | if (isNewPropTypes) { 182 | sb.append("\n\n"); 183 | } 184 | sb.append(componentName).append(".propTypes = {\n"); 185 | } 186 | for (int i = 0; i < beans.size(); i++) { 187 | sb.append(propsBlank).append(beans.get(i).name).append(": PropTypes."); 188 | if ("shape".equals(beans.get(i).type) || "exact".equals(beans.get(i).type)) { 189 | List shapePropList = beans.get(i).getShapePropTypeList(); 190 | sb.append(beans.get(i).type); 191 | if (shapePropList != null) { 192 | int shapePropSize = shapePropList.size(); 193 | sb.append(shapePropSize > 0 ? "({\n" : "()"); 194 | for (int j = 0; j < shapePropSize; j++) { 195 | sb.append(propsBlank).append(PropTypesHelper.getBlank(indent)).append(shapePropList.get(j).name) 196 | .append(": PropTypes.").append(shapePropList.get(j).type); 197 | final String propType = shapePropList.get(j).type; 198 | boolean isSpecial = Arrays.stream(SpecialPropTypes.values()).anyMatch(o -> o.name().equals(propType)); 199 | if (isSpecial) { 200 | sb.append("("); 201 | if(shapePropList.get(j).getJsonData() != null){ 202 | sb.append(shapePropList.get(j).getJsonData()); 203 | } 204 | sb.append(")"); 205 | } 206 | if (shapePropList.get(j).isRequired) { 207 | sb.append(".isRequired"); 208 | } 209 | if (j < shapePropList.size() - 1) sb.append(",\n"); 210 | } 211 | if (shapePropSize > 0) { 212 | sb.append("\n").append(propsBlank).append("})"); 213 | } 214 | } 215 | } else { 216 | final String propType = beans.get(i).type; 217 | boolean isSpecial = Arrays.stream(SpecialPropTypes.values()).anyMatch(o -> o.name().equals(propType)); 218 | sb.append(beans.get(i).type); 219 | if (isSpecial) { 220 | sb.append("("); 221 | if(beans.get(i).getJsonData() != null){ 222 | sb.append(beans.get(i).getJsonData()); 223 | } 224 | sb.append(")"); 225 | } 226 | } 227 | if (beans.get(i).isRequired) { 228 | sb.append(".isRequired"); 229 | } 230 | if (i < beans.size() - 1) sb.append(",\n"); 231 | } 232 | sb.append("\n").append(propsObjBlank).append("}"); 233 | 234 | if (!noSemiColons) { 235 | sb.append(";"); 236 | } 237 | return sb.toString(); 238 | } 239 | 240 | private String getInsertDefaultPropsString(String componentName, List beans, 241 | boolean isNewPropTypes, boolean isES7) { 242 | int indent = settingService.getState().getIndent(); 243 | boolean noSemiColons = settingService.getState().isNoSemiColons(); 244 | StringBuilder sb = new StringBuilder(); 245 | String propsObjBlank = isES7 ? " " : ""; 246 | String propsBlank = propsObjBlank + PropTypesHelper.getBlank(indent); 247 | if (isES7) { 248 | sb.append("static defaultProps = {\n"); 249 | } else { 250 | if (isNewPropTypes) { 251 | sb.append("\n\n"); 252 | } 253 | sb.append(componentName).append(".defaultProps = {\n"); 254 | } 255 | for (int i = 0; i < beans.size(); i++) { 256 | sb.append(propsBlank).append(beans.get(i).name).append(": ").append(beans.get(i).getDefaultValue()); 257 | if (i < beans.size() - 1) sb.append(",\n"); 258 | } 259 | sb.append("\n").append(propsObjBlank).append("}"); 260 | 261 | if (!noSemiColons) { 262 | sb.append(";"); 263 | } 264 | return sb.toString(); 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/com/suming/plugin/CommonAction.java: -------------------------------------------------------------------------------- 1 | package com.suming.plugin; 2 | 3 | import com.intellij.codeInsight.hint.HintManager; 4 | import com.intellij.lang.ecmascript6.psi.ES6Class; 5 | import com.intellij.lang.ecmascript6.psi.ES6FromClause; 6 | import com.intellij.lang.ecmascript6.psi.ES6ImportDeclaration; 7 | import com.intellij.lang.javascript.psi.*; 8 | import com.intellij.lang.javascript.psi.ecma6.impl.ES6FieldImpl; 9 | import com.intellij.lang.javascript.psi.impl.JSDestructuringElementImpl; 10 | import com.intellij.lang.javascript.psi.impl.JSDestructuringObjectImpl; 11 | import com.intellij.lang.javascript.psi.impl.JSReferenceExpressionImpl; 12 | import com.intellij.lang.javascript.psi.types.JSContext; 13 | import com.intellij.openapi.actionSystem.AnAction; 14 | import com.intellij.openapi.actionSystem.AnActionEvent; 15 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 16 | import com.intellij.openapi.application.ApplicationManager; 17 | import com.intellij.openapi.command.CommandProcessor; 18 | import com.intellij.openapi.components.ServiceManager; 19 | import com.intellij.openapi.editor.Caret; 20 | import com.intellij.openapi.editor.Editor; 21 | import com.intellij.openapi.editor.SelectionModel; 22 | import com.intellij.openapi.project.Project; 23 | import com.intellij.openapi.util.TextRange; 24 | import com.intellij.psi.PsiComment; 25 | import com.intellij.psi.PsiElement; 26 | import com.intellij.psi.PsiFile; 27 | import com.intellij.psi.PsiWhiteSpace; 28 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 29 | import com.intellij.psi.util.PsiTreeUtil; 30 | import com.intellij.psi.xml.XmlElement; 31 | import com.suming.plugin.bean.Component; 32 | import com.suming.plugin.bean.ComponentType; 33 | import com.suming.plugin.bean.ESVersion; 34 | import com.suming.plugin.bean.PropTypeBean; 35 | import com.suming.plugin.constants.ArrayFunctions; 36 | import com.suming.plugin.persist.SettingService; 37 | import com.suming.plugin.utils.PropTypesHelper; 38 | import com.suming.plugin.utils.SelectWordUtilCompat; 39 | import org.apache.http.util.TextUtils; 40 | import org.jetbrains.annotations.NotNull; 41 | import org.jetbrains.annotations.Nullable; 42 | 43 | import java.util.*; 44 | import java.util.stream.Collectors; 45 | 46 | abstract class CommonAction extends AnAction { 47 | SettingService settingService = ServiceManager.getService(SettingService.class); 48 | 49 | void runCommand(Project project, final Runnable runnable) { 50 | CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(runnable), "inject PropTypes", null); 51 | } 52 | 53 | @Override 54 | public void actionPerformed(AnActionEvent e) { 55 | Project project = getEventProject(e); 56 | Editor editor = e.getData(PlatformDataKeys.EDITOR); 57 | PsiFile file = e.getData(PlatformDataKeys.PSI_FILE); 58 | Caret caret = e.getData(PlatformDataKeys.CARET); 59 | if (project == null || editor == null || file == null || caret == null) { 60 | return; 61 | } 62 | 63 | final String selectedText = getSelectedText(editor); 64 | if (selectedText == null) { 65 | showHint(editor, "you must select the text as a Component's name"); 66 | return; 67 | } 68 | 69 | Component component = getSelectComponent(selectedText, file); 70 | if (component == null) { 71 | showHint(editor, "The selected text is not a valid React Component "); 72 | return; 73 | } 74 | // find all use propTypes 75 | List usePropNameList = findPropsNameList(component); 76 | 77 | // create a empty list add use propTypes 78 | List newPropNameList = new ArrayList<>(usePropNameList); 79 | 80 | // filter exist list 81 | PsiElement expression = getPropTypeElementByName(file, selectedText); 82 | if (expression != null) { 83 | List existPropNameList = findPropsNameListInPropTypeObject(expression); 84 | if (usePropNameList.size() == 0) { 85 | newPropNameList.addAll(existPropNameList); 86 | } else { 87 | for (PropTypeBean propTypeBean : existPropNameList) { 88 | for (int i = 0; i < usePropNameList.size(); i++) { 89 | PropTypeBean usePropType = usePropNameList.get(i); 90 | if (usePropType.name.equals(propTypeBean.name)) { 91 | if (TextUtils.isEmpty(usePropType.type) || usePropType.type.equals("any")) { 92 | usePropType.setType(propTypeBean.type); 93 | } 94 | usePropType.setRequired(propTypeBean.isRequired); 95 | usePropType.setShapePropTypeList(propTypeBean.getShapePropTypeList()); 96 | usePropType.setJsonData(propTypeBean.getJsonData()); 97 | break; 98 | } 99 | if (i == usePropNameList.size() - 1) { 100 | newPropNameList.add(0, propTypeBean); 101 | } 102 | } 103 | } 104 | } 105 | if (expression instanceof ES6FieldImpl) { 106 | component.setEsVersion(ESVersion.ES7); 107 | } else { 108 | component.setEsVersion(ESVersion.ES6); 109 | } 110 | } 111 | 112 | // filter default propTypes 113 | List defaultPropTypeList = findPropsNameListInDefaultPropsElement(getDefaultPropsElementByName(file, selectedText)); 114 | for (PropTypeBean defaultPropType : defaultPropTypeList) { 115 | for (int i = 0; i < newPropNameList.size(); i++) { 116 | if (newPropNameList.get(i).name.equals(defaultPropType.name)) { 117 | PropTypeBean newProp = newPropNameList.get(i); 118 | if (TextUtils.isEmpty(newProp.type) || newProp.type.equals("any")) { 119 | newPropNameList.get(i).setType(defaultPropType.type); 120 | } 121 | newPropNameList.get(i).setDefaultValue(defaultPropType.getDefaultValue()); 122 | break; 123 | } 124 | if (i == newPropNameList.size() - 1) { 125 | PropTypeBean bean = new PropTypeBean(defaultPropType.name, defaultPropType.type, 126 | false, defaultPropType.getDefaultValue()); 127 | newPropNameList.add(bean); 128 | } 129 | } 130 | } 131 | 132 | actionPerformed(project, editor, file, selectedText, newPropNameList, component); 133 | } 134 | 135 | abstract void actionPerformed(Project project, Editor editor, PsiFile file, String selectedText, 136 | List propNameList, Component component); 137 | 138 | @Nullable 139 | String getSelectedText(Editor editor) { 140 | SelectionModel selectionModel = editor.getSelectionModel(); 141 | if (selectionModel.hasSelection()) { 142 | return selectionModel.getSelectedText(); 143 | } else { 144 | final ArrayList ranges = new ArrayList<>(); 145 | final int offset = editor.getCaretModel().getOffset(); 146 | SelectWordUtilCompat.addWordOrLexemeSelection(false, editor, offset, ranges, SelectWordUtilCompat.JAVASCRIPT_IDENTIFIER_PART_CONDITION); 147 | if (ranges.size() > 0) { 148 | return editor.getDocument().getText(ranges.get(0)); 149 | } else { 150 | return null; 151 | } 152 | } 153 | } 154 | 155 | void showHint(Editor editor, String s) { 156 | HintManager.getInstance().showErrorHint(editor, s); 157 | } 158 | 159 | @Nullable 160 | ES6Class getSelectES6Component(String selectText, PsiFile file) { 161 | return PsiTreeUtil.findChildrenOfType(file, JSReferenceExpression.class) 162 | .stream() 163 | .filter(o -> o.getText().equals(selectText)) 164 | .filter(o -> o.getParent() instanceof ES6Class) 165 | .map(o -> (ES6Class) o.getParent()) 166 | .findFirst() 167 | .orElse(null); 168 | } 169 | 170 | @Nullable 171 | private JSFunction getSelectStatelessComponent(String selectText, PsiFile file) { 172 | return PsiTreeUtil.findChildrenOfType(file, JSFunction.class) 173 | .stream() 174 | .filter(o -> o.getName() != null && o.getName().equals(selectText)) 175 | .filter(o -> { 176 | if (this.settingService.getState().isUncheckFunctionalComponent()) { 177 | return true; 178 | } else { 179 | XmlElement element = PsiTreeUtil.findChildrenOfType(o, XmlElement.class) 180 | .stream() 181 | .findFirst() 182 | .orElse(null); 183 | return element != null; 184 | } 185 | }) 186 | .findFirst() 187 | .orElse(null); 188 | 189 | } 190 | 191 | @Nullable 192 | private Component getSelectComponent(String selectText, PsiFile file) { 193 | ES6Class es6Class = getSelectES6Component(selectText, file); 194 | if (es6Class != null) { 195 | return new Component(es6Class, ComponentType.STANDARD); 196 | } else { 197 | JSFunction statelessElement = getSelectStatelessComponent(selectText, file); 198 | if (statelessElement != null) { 199 | return new Component(statelessElement, ComponentType.STATELESS, ESVersion.ES6); 200 | } else { 201 | return null; 202 | } 203 | } 204 | } 205 | 206 | @Nullable 207 | boolean hasImportPropTypes(PsiFile file) { 208 | boolean hasNew = PsiTreeUtil.findChildrenOfType(file, ES6FromClause.class) 209 | .stream() 210 | .filter(o -> o.getText().contains("\'prop-types\'")) 211 | .map(Objects::nonNull) 212 | .reduce(false, (a, b) -> a || b); 213 | boolean hasOld = PsiTreeUtil.findChildrenOfType(file, ES6FromClause.class) 214 | .stream() 215 | .filter(o -> o.getText().contains("\'react\'")) 216 | .filter(o -> o.getParent().getText().contains("PropTypes")) 217 | .map(Objects::nonNull) 218 | .reduce(false, (a, b) -> a || b); 219 | return hasNew || hasOld; 220 | } 221 | 222 | @Nullable 223 | ES6ImportDeclaration getReactImportDeclaration(PsiFile file) { 224 | return PsiTreeUtil.findChildrenOfType(file, ES6FromClause.class) 225 | .stream() 226 | .filter(o -> o.getText().contains("\'react\'")) 227 | .filter(o -> o.getParent() instanceof ES6ImportDeclaration) 228 | .map(o -> (ES6ImportDeclaration) o.getParent()) 229 | .findFirst() 230 | .orElse(null); 231 | 232 | } 233 | 234 | int findFirstImportIndex(PsiFile file) { 235 | PsiElement[] children = file.getChildren(); 236 | for (PsiElement aChildren : children) { 237 | if (aChildren instanceof PsiWhiteSpace) { 238 | continue; 239 | } 240 | if (aChildren instanceof PsiComment) { 241 | continue; 242 | } 243 | return aChildren.getTextRange().getStartOffset(); 244 | } 245 | return 0; 246 | } 247 | 248 | @NotNull 249 | private List findPropsNameList(Component component) { 250 | PsiElement psiElement = component.getElement(); 251 | ComponentType componentType = component.getComponentType(); 252 | List paramList = new ArrayList<>(); 253 | if (componentType == ComponentType.STATELESS) { 254 | JSParameterList jsParameterList = ((JSFunction) psiElement).getParameterList(); 255 | JSParameterListElement propsParam = (jsParameterList != null && jsParameterList.getParameters().length > 0) ? 256 | jsParameterList.getParameters()[0] : null; 257 | if (propsParam != null) { 258 | if (propsParam instanceof JSDestructuringParameter) { 259 | JSDestructuringElement parent = (JSDestructuringElement) propsParam; 260 | JSDestructuringObject destructuringObject = (JSDestructuringObject) parent.getFirstChild(); 261 | paramList.addAll(getPropsWithDestructuringProperty(destructuringObject, parent.getParent().getParent())); 262 | } else { 263 | paramList.addAll(findPropsNameListByPropsIdentity(propsParam.getName(), psiElement)); 264 | } 265 | } 266 | } else if (componentType == ComponentType.STANDARD) { 267 | paramList.addAll(findPropsNameListByPropsIdentity("props", psiElement)); 268 | paramList.addAll(findPropsNameListByPropsIdentity("nextProps", psiElement)); 269 | } else { 270 | // ES5 is not supported for the time being 271 | } 272 | // maybe have duplicate data, so must distinct 273 | HashSet reverseSet = new HashSet<>(); 274 | reverseSet.add(1); 275 | reverseSet.add(3); 276 | return paramList.stream() 277 | .sorted(PropTypesHelper.sortByKey(reverseSet, PropTypeBean::getName, PropTypeBean::getType, 278 | PropTypeBean::getDefaultValue, PropTypeBean::isRequired)) 279 | .filter(PropTypesHelper.distinctByKey(PropTypeBean::getName)) 280 | .collect(Collectors.toList()); 281 | } 282 | 283 | 284 | @NotNull 285 | private List getPropsWithDestructuringProperty(JSDestructuringObject destructuringObject, 286 | PsiElement parentElement) { 287 | List propTypeBeans = new ArrayList<>(); 288 | PsiElement[] elements = destructuringObject.getChildren(); 289 | for (PsiElement element : elements) { 290 | if (element instanceof JSDestructuringProperty) { 291 | JSDestructuringProperty property = (JSDestructuringProperty) element; 292 | JSInitializerOwner owner = property.getDestructuringElement(); 293 | PsiElement firstChild = property.getFirstChild(); 294 | JSExpression initializer = owner != null ? owner.getInitializer() : null; 295 | if (firstChild.getText().equals("...")) { 296 | // prevent loop 297 | propTypeBeans.addAll(findPropsNameListWithIdentityReference(property.getName(), parentElement)); 298 | } else if (firstChild instanceof JSVariable) { 299 | PropTypeBean bean = new PropTypeBean(property.getName()); 300 | // only setting's inferByDestructure equal true 301 | if (settingService.getState().isInferByDestructure()) { 302 | if (initializer != null) { 303 | bean.setType(PropTypesHelper.getPropTypeByValue(initializer.getText())); 304 | bean.setDefaultValue(initializer.getText()); 305 | } else if (owner != null) { 306 | String ownerStr = owner.getText(); 307 | if (ownerStr.startsWith("{") && ownerStr.endsWith("}")) { 308 | bean.setType("object"); 309 | } 310 | } 311 | } 312 | propTypeBeans.add(findPropsNameTypeAndSetType(bean, null, parentElement)); 313 | } 314 | } 315 | } 316 | return propTypeBeans; 317 | } 318 | 319 | @NotNull 320 | private List findPropsNameListByPropsIdentity(String identity, PsiElement psiElement) { 321 | // maybe contains default value, so find first 322 | List destructuringParamList = PsiTreeUtil.findChildrenOfType(psiElement, LeafPsiElement.class) 323 | .stream() 324 | .filter(o -> o.getText().equals(identity)) 325 | .filter(o -> o.getElementType().toString().equals("JS:IDENTIFIER")) 326 | .filter(o -> { 327 | if (o.getParent() instanceof JSReferenceExpressionImpl) { 328 | JSReferenceExpressionImpl parent = (JSReferenceExpressionImpl) o.getParent(); 329 | if (parent.getParent() instanceof JSDestructuringElementImpl 330 | && parent.getParent().getFirstChild() instanceof JSDestructuringObjectImpl 331 | && parent.getParent().getParent() != null) { 332 | return true; 333 | } 334 | } 335 | return false; 336 | }) 337 | .map(o -> { 338 | JSDestructuringElementImpl parent = (JSDestructuringElementImpl) o.getParent().getParent(); 339 | JSDestructuringObject destructuringObject = (JSDestructuringObject) parent.getFirstChild(); 340 | return getPropsWithDestructuringProperty(destructuringObject, parent.getParent().getParent()); 341 | }) 342 | .reduce(new ArrayList<>(), (a, b) -> { 343 | a.addAll(b); 344 | return a; 345 | }); 346 | List paramList = findPropsNameListWithIdentityReference(identity, psiElement); 347 | paramList.addAll(destructuringParamList); 348 | return paramList; 349 | } 350 | 351 | @NotNull 352 | private PropTypeBean findPropsNameTypeAndSetType(PropTypeBean bean, @Nullable String identity, PsiElement psiElement) { 353 | // disable infer type 354 | if (!settingService.getState().isInferByPropsCall()) return bean; 355 | 356 | String firstReg = identity != null ? ".*\\s*" + identity + "\\s*\\.\\s*" : ""; 357 | String funcReg = firstReg + bean.name + "\\s*\\(" + ".*"; 358 | 359 | boolean isFunc = PsiTreeUtil.findChildrenOfType(psiElement, JSCallExpression.class) 360 | .stream() 361 | .anyMatch(o -> o.getText().matches(funcReg)); 362 | // only infer func and array 363 | if (isFunc) { 364 | bean.setType("func"); 365 | } else { 366 | String arrayFuncString = Arrays.stream(ArrayFunctions.values()) 367 | .map(o -> (o.name())) 368 | .reduce("", (a, b) -> a + '|' + b); 369 | String arrayReg = firstReg + bean.name + "\\s*\\.\\s*" + arrayFuncString.replaceFirst("^\\|", "(") + ")" + ".*"; 370 | String objectReq = firstReg + bean.name + "\\s*\\.\\s*.*"; 371 | boolean isObject = PsiTreeUtil.findChildrenOfType(psiElement, JSCallExpression.class) 372 | .stream() 373 | .anyMatch(o -> o.getText().matches(objectReq)); 374 | if (isObject) { 375 | boolean isArray = PsiTreeUtil.findChildrenOfType(psiElement, JSCallExpression.class) 376 | .stream() 377 | .anyMatch(o -> o.getText().matches(arrayReg)); 378 | if (isArray) { 379 | bean.setType("array"); 380 | } else { 381 | bean.setType("object"); 382 | } 383 | } 384 | } 385 | return bean; 386 | } 387 | 388 | @NotNull 389 | private List findPropsNameListWithIdentityReference(String identity, PsiElement psiElement) { 390 | return PsiTreeUtil.findChildrenOfType(psiElement, LeafPsiElement.class) 391 | .stream() 392 | .filter(o -> o.getText().equals(identity)) 393 | .filter(o -> o.getElementType().toString().equals("JS:IDENTIFIER")) 394 | .filter(o -> { 395 | if (o.getParent() instanceof JSReferenceExpressionImpl) { 396 | JSReferenceExpressionImpl parent = (JSReferenceExpressionImpl) o.getParent(); 397 | if (parent.getTreeNext() != null && parent.getTreeNext().getElementType().toString().equals("JS:DOT") 398 | && parent.getTreeNext().getTreeNext() != null) { 399 | return true; 400 | } 401 | } 402 | return false; 403 | }) 404 | .map(o -> ((JSReferenceExpressionImpl) o.getParent()).getTreeNext().getTreeNext().getText()) 405 | .distinct() 406 | .map(o -> findPropsNameTypeAndSetType(new PropTypeBean(o, "any", false), identity, psiElement)) 407 | .collect(Collectors.toList()); 408 | } 409 | 410 | 411 | @Nullable 412 | PsiElement getES7FieldElementByName(PsiFile file, String componentName, String fieldName) { 413 | ES6Class es6Class = getSelectES6Component(componentName, file); 414 | if (es6Class == null) return null; 415 | return PsiTreeUtil.findChildrenOfType(es6Class, ES6FieldImpl.class) 416 | .stream() 417 | .filter(o -> Objects.equals(o.getName(), fieldName)) 418 | .filter(o -> o.getJSContext() == JSContext.STATIC) 419 | .findFirst() 420 | .orElse(null); 421 | } 422 | 423 | @Nullable 424 | PsiElement getES6FieldByName(PsiFile file, String componentName, String fieldName) { 425 | return PsiTreeUtil.findChildrenOfType(file, JSDefinitionExpression.class) 426 | .stream() 427 | .filter(o -> o.getText().matches(componentName + "\\s*\\.\\s*" + fieldName)) 428 | .filter(o -> o.getParent() instanceof JSAssignmentExpression) 429 | .map(o -> (JSAssignmentExpression) o.getParent()) 430 | .filter(o -> o.getLastChild() instanceof JSObjectLiteralExpression) 431 | .findFirst() 432 | .orElse(null); 433 | } 434 | 435 | @Nullable 436 | private PsiElement getES5PropTypeElementByName(PsiFile file, String componentName) { 437 | return null; 438 | } 439 | 440 | /** 441 | * Find defaultProps Object and to PropTypeBean List 442 | * 443 | * @param expression 444 | * @return 445 | */ 446 | @NotNull 447 | List findPropsNameListInDefaultPropsElement(PsiElement expression) { 448 | List paramList = new ArrayList<>(); 449 | if (expression != null && expression.getLastChild() != null && expression.getLastChild() instanceof JSObjectLiteralExpression) { 450 | JSObjectLiteralExpression literalExpression = (JSObjectLiteralExpression) expression.getLastChild(); 451 | JSProperty[] properties = literalExpression.getProperties(); 452 | for (JSProperty property : properties) { 453 | String name = property.getName(); 454 | String value = property.getValue() != null ? property.getValue().getText() : ""; 455 | boolean inferByDefaultProps = settingService.getState().isInferByDefaultProps(); 456 | PropTypeBean bean = new PropTypeBean(name, inferByDefaultProps ? PropTypesHelper.getPropTypeByValue(value) : "any", false); 457 | bean.setDefaultValue(value); 458 | paramList.add(bean); 459 | } 460 | } 461 | return paramList; 462 | } 463 | 464 | /** 465 | * Find defaultProps Object from a JS File with componentName 466 | * 467 | * @param file 468 | * @param componentName 469 | * @return 470 | */ 471 | @Nullable 472 | private PsiElement getDefaultPropsElementByName(PsiFile file, String componentName) { 473 | PsiElement es7Element = getES7FieldElementByName(file, componentName, "defaultProps"); 474 | if (es7Element == null) { 475 | PsiElement es6Element = getES6FieldByName(file, componentName, "defaultProps"); 476 | if (es6Element == null) { 477 | return getES5PropTypeElementByName(file, componentName); 478 | } else { 479 | return es6Element; 480 | } 481 | } else { 482 | return es7Element; 483 | } 484 | } 485 | 486 | /** 487 | * Find propTypes Object and to PropTypeBean List 488 | * 489 | * @param expression 490 | * @return 491 | */ 492 | @NotNull 493 | List findPropsNameListInPropTypeObject(PsiElement expression) { 494 | List paramList = new ArrayList<>(); 495 | if (expression != null && expression.getLastChild() != null && expression.getLastChild() instanceof JSObjectLiteralExpression) { 496 | JSObjectLiteralExpression literalExpression = (JSObjectLiteralExpression) expression.getLastChild(); 497 | JSProperty[] properties = literalExpression.getProperties(); 498 | for (JSProperty property : properties) { 499 | if (property.getName() != null && property.getLastChild().getText().contains("PropTypes")) { 500 | PropTypeBean bean = new PropTypeBean(property.getName()); 501 | PropTypesHelper.updatePropTypeFromCode(bean, property.getLastChild().getText()); 502 | paramList.add(bean); 503 | } 504 | 505 | } 506 | } 507 | return paramList; 508 | } 509 | 510 | /** 511 | * Find propTypes Object from a JS File with componentName 512 | * 513 | * @param file 514 | * @param componentName 515 | * @return 516 | */ 517 | @Nullable 518 | private PsiElement getPropTypeElementByName(PsiFile file, String componentName) { 519 | // ES7 is ES6Field , ES6 is JSDefinitionExpression, ES5 is JSField 520 | PsiElement es7Element = getES7FieldElementByName(file, componentName, "propTypes"); 521 | if (es7Element == null) { 522 | PsiElement es6Element = getES6FieldByName(file, componentName, "propTypes"); 523 | if (es6Element == null) { 524 | return getES5PropTypeElementByName(file, componentName); 525 | } else { 526 | return es6Element; 527 | } 528 | } else { 529 | return es7Element; 530 | } 531 | } 532 | } 533 | --------------------------------------------------------------------------------