package com.wix.rt.settings; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.ide.projectView.ProjectView; import com.intellij.javascript.nodejs.NodeDetectionUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiManager; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.HyperlinkLabel; import com.intellij.ui.TextFieldWithHistory; import com.intellij.ui.TextFieldWithHistoryWithBrowseButton; import com.intellij.util.NotNullProducer; import com.intellij.util.ui.UIUtil; import com.intellij.webcore.ui.SwingHelper; import com.wix.rt.RTProjectComponent; import com.wix.rt.cli.RTFinder; import com.wix.rtk.cli.RTRunner; import com.wix.rtk.cli.RTSettings; import com.wix.settings.ValidationInfo; import com.wix.settings.ValidationUtils; import com.wix.settings.Validator; import com.wix.ui.PackagesNotificationPanel; import com.wix.utils.FileUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.File; import java.util.List; public class RTSettingsPage implements Configurable { private static final String FIX_IT = "Fix it"; private static final String HOW_TO_USE_RT = "How to Use React-Templates"; private static final String HOW_TO_USE_LINK = "https://github.com/wix/react-templates"; private JCheckBox pluginEnabledCheckbox; private JPanel panel; private JPanel errorPanel; private TextFieldWithHistoryWithBrowseButton rtBinField; private TextFieldWithHistoryWithBrowseButton nodeInterpreterField; private HyperlinkLabel usageLink; private JLabel pathToRTBinLabel; private JLabel nodeInterpreterLabel; private JLabel versionLabel; private JCheckBox groupController; private JRadioButton noneGlobalsRadioButton; private JRadioButton AMDRadioButton; private JRadioButton commonJSRadioButton; private JPanel modulesPanel; private JLabel modulesLabel; private JCheckBox groupOtherFilesCheckBox; private JRadioButton ES6RadioButton; private JRadioButton typescriptRadioButton; private JComboBox<String> comboBoxTargetVersion; private JLabel targetVersionLabel; private JCheckBox reactNativeCheckBox; private JCheckBox watchAndCompileRtCheckBox; private final PackagesNotificationPanel packagesNotificationPanel; protected final Project project; private final String basePath; public RTSettingsPage(@NotNull final Project project) { this.project = project; basePath = project.getBasePath(); configRTBinField(); configNodeField(); pluginEnabledCheckbox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { boolean enabled = e.getStateChange() == ItemEvent.SELECTED; setEnabledState(enabled); if (enabled) { List<File> newFiles = NodeDetectionUtil.listAllPossibleNodeInterpreters(); if (!newFiles.isEmpty() && nodeInterpreterField.getChildComponent().getText().isEmpty()) { nodeInterpreterField.getChildComponent().setText(newFiles.get(0).getAbsolutePath()); } List<File> rtBinFiles = RTFinder.searchForRTBin(getProjectPath()); if (!rtBinFiles.isEmpty() && rtBinField.getChildComponent().getText().isEmpty()) { rtBinField.getChildComponent().setText(rtBinFiles.get(0).getAbsolutePath()); } } } }); // System.out.println(String.format("", "")); this.packagesNotificationPanel = new PackagesNotificationPanel(project); // GridConstraints gridConstraints = new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, // GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, // null, new Dimension(250, 150), null); errorPanel.add(this.packagesNotificationPanel.getComponent(), BorderLayout.CENTER); DocumentAdapter docAdp = new DocumentAdapter() { protected void textChanged(DocumentEvent e) { updateLaterInEDT(); } @Override public void changedUpdate(DocumentEvent e) { super.changedUpdate(e); // System.out.println("changedUpdate " + rtBinField.getText()); } @Override public void insertUpdate(DocumentEvent e) { super.insertUpdate(e); // System.out.println("insertUpdate " + rtBinField.getText()); } @Override public void removeUpdate(DocumentEvent e) { // super.removeUpdate(e); // e.getDocument(). System.out.println("removeUpdate " + rtBinField.getText()); } }; // rtBinField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); // nodeInterpreterField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); rtBinField.getChildComponent().addDocumentListener(docAdp); nodeInterpreterField.getChildComponent().addDocumentListener(docAdp); } private File getProjectPath() { return new File(basePath); } private void updateLaterInEDT() { UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { RTSettingsPage.this.update(); } }); } private void update() { ApplicationManager.getApplication().assertIsDispatchThread(); validate(); } private String getModules() { if (AMDRadioButton.isSelected()) { return RTRunner.AMD; } if (commonJSRadioButton.isSelected()) { return RTRunner.COMMONJS; } if (ES6RadioButton.isSelected()) { return RTRunner.ES6; } if (typescriptRadioButton.isSelected()) { return RTRunner.TYPESCRIPT; } return RTRunner.NONE; } private void setEnabledState(boolean enabled) { rtBinField.setEnabled(enabled); nodeInterpreterField.setEnabled(enabled); pathToRTBinLabel.setEnabled(enabled); nodeInterpreterLabel.setEnabled(enabled); modulesPanel.setEnabled(enabled); groupController.setEnabled(enabled); commonJSRadioButton.setEnabled(enabled); noneGlobalsRadioButton.setEnabled(enabled); AMDRadioButton.setEnabled(enabled); ES6RadioButton.setEnabled(enabled); typescriptRadioButton.setEnabled(enabled); comboBoxTargetVersion.setEnabled(enabled); targetVersionLabel.setEnabled(enabled); modulesLabel.setEnabled(enabled); reactNativeCheckBox.setEnabled(enabled); watchAndCompileRtCheckBox.setEnabled(enabled); groupOtherFilesCheckBox.setEnabled(enabled); } private void validateField(Validator validator, TextFieldWithHistoryWithBrowseButton field, boolean allowEmpty, String message) { if (!ValidationUtils.validatePath(project, field.getChildComponent().getText(), allowEmpty)) { ValidationInfo error = new ValidationInfo(field.getChildComponent().getTextEditor(), message, FIX_IT); validator.errors.add(error); } } private void validate() { if (!pluginEnabledCheckbox.isSelected()) { return; } Validator validator = new Validator(); validateField(validator, rtBinField, false, "Path to react-templates is invalid {{LINK}}"); validateField(validator, nodeInterpreterField, false, "Path to node interpreter is invalid {{LINK}}"); // if (!validateDirectory(customRulesPathField.getText(), true)) { // RTValidationInfo error = new RTValidationInfo(customRulesPathField, "Path to custom rules is invalid {{LINK}}", FIX_IT); // errors.add(error); // } // if (!validateDirectory(rulesPathField.getChildComponent().getText(), true)) { // RTValidationInfo error = new RTValidationInfo(rulesPathField.getChildComponent().getTextEditor(), "Path to rules is invalid {{LINK}}", FIX_IT); // errors.add(error); // } if (validator.isEmpty()) { getVersion(); } packagesNotificationPanel.processErrors(validator); } private RTSettings settings; private void getVersion() { if (settings != null && areEqual(nodeInterpreterField, settings.getNode()) && areEqual(rtBinField, settings.getRtExe()) && settings.getCwd().equals(basePath) ) { return; } System.out.println(basePath); String node = nodeInterpreterField.getChildComponent().getText(); String rtBin = rtBinField.getChildComponent().getText(); System.out.println("node: " + node + " rtBin: " + rtBin); settings = RTSettings.Companion.buildVersion(basePath, node, rtBin); // settings.node = nodeInterpreterField.getChildComponent().getText(); // settings.rtExecutablePath = rtBinField.getChildComponent().getText(); // settings.cwd = basePath; try { String version = RTRunner.INSTANCE.runVersion(settings); versionLabel.setText(version.trim()); List<String> versions = RTRunner.INSTANCE.listTargetVersions(settings); String[] array = new String[versions.size()]; comboBoxTargetVersion.setModel(new DefaultComboBoxModel<String>(versions.toArray(array))); // for (String s : versions) { // comboBoxTargetVersion.addItem(s); // } } catch (Exception e) { e.printStackTrace(); } } private static TextFieldWithHistory configWithDefaults(TextFieldWithHistoryWithBrowseButton field) { TextFieldWithHistory textFieldWithHistory = field.getChildComponent(); textFieldWithHistory.setHistorySize(-1); textFieldWithHistory.setMinimumAndPreferredWidth(0); return textFieldWithHistory; } private void configRTBinField() { configWithDefaults(rtBinField); SwingHelper.addHistoryOnExpansion(rtBinField.getChildComponent(), new NotNullProducer<List<String>>() { @NotNull public List<String> produce() { List<File> newFiles = RTFinder.searchForRTBin(getProjectPath()); return FileUtils.toAbsolutePath(newFiles); } }); SwingHelper.installFileCompletionAndBrowseDialog(project, rtBinField, "Select React-Templates Cli", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()); } private void configNodeField() { TextFieldWithHistory textFieldWithHistory = configWithDefaults(nodeInterpreterField); SwingHelper.addHistoryOnExpansion(textFieldWithHistory, new NotNullProducer<List<String>>() { @NotNull public List<String> produce() { List<File> newFiles = NodeDetectionUtil.listAllPossibleNodeInterpreters(); return FileUtils.toAbsolutePath(newFiles); } }); SwingHelper.installFileCompletionAndBrowseDialog(project, nodeInterpreterField, "Select Node Interpreter", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()); } @Nls @Override public String getDisplayName() { return "React-Templates"; } @Nullable @Override public String getHelpTopic() { return null; } @Nullable @Override public JComponent createComponent() { loadSettings(); return panel; } private static boolean areEqual(TextFieldWithHistoryWithBrowseButton field, String value) { return field.getChildComponent().getText().equals(value); } private static boolean areEqual(JComboBox<?> field, String value) { Object item = field.getSelectedItem(); return item == null && value == null || item != null && item.equals(value); } private static boolean areEqual(JCheckBox field, boolean value) { return field.isSelected() == value; } @Override public boolean isModified() { Settings s = getSettings(); return !areEqual(pluginEnabledCheckbox, s.pluginEnabled) || !areEqual(rtBinField, s.rtExecutable) || !getModules().equals(s.modules) || !areEqual(groupController, s.groupController) || !areEqual(groupOtherFilesCheckBox, s.groupOther) || !areEqual(watchAndCompileRtCheckBox, s.watchAndCompileRT) || !areEqual(reactNativeCheckBox, s.reactNative) || !areEqual(comboBoxTargetVersion, s.targetVersion) || !areEqual(nodeInterpreterField, s.nodeInterpreter); } @Override public void apply() throws ConfigurationException { saveSettings(); PsiManager.getInstance(project).dropResolveCaches(); ProjectView.getInstance(project).refresh(); } protected void saveSettings() { Settings settings = getSettings(); settings.pluginEnabled = pluginEnabledCheckbox.isSelected(); settings.rtExecutable = rtBinField.getChildComponent().getText(); settings.nodeInterpreter = nodeInterpreterField.getChildComponent().getText(); settings.modules = getModules(); settings.groupController = groupController.isSelected(); settings.groupOther = groupOtherFilesCheckBox.isSelected(); settings.reactNative = reactNativeCheckBox.isSelected(); settings.groupOther = groupOtherFilesCheckBox.isSelected(); settings.watchAndCompileRT = watchAndCompileRtCheckBox.isSelected(); settings.targetVersion = (String) comboBoxTargetVersion.getSelectedItem(); project.getComponent(RTProjectComponent.class).validateSettings(); DaemonCodeAnalyzer.getInstance(project).restart(); } private void loadSettings() { Settings settings = getSettings(); pluginEnabledCheckbox.setSelected(settings.pluginEnabled); rtBinField.getChildComponent().setText(settings.rtExecutable); nodeInterpreterField.getChildComponent().setText(settings.nodeInterpreter); if (settings.modules.equals(RTRunner.AMD)) { AMDRadioButton.setSelected(true); } else if (settings.modules.equals(RTRunner.COMMONJS)) { commonJSRadioButton.setSelected(true); } else if (settings.modules.equals(RTRunner.TYPESCRIPT)) { typescriptRadioButton.setSelected(true); } else if (settings.modules.equals(RTRunner.ES6)) { ES6RadioButton.setSelected(true); } else { noneGlobalsRadioButton.setSelected(true); } groupController.setSelected(settings.groupController); reactNativeCheckBox.setSelected(settings.reactNative); watchAndCompileRtCheckBox.setSelected(settings.watchAndCompileRT); groupOtherFilesCheckBox.setSelected(settings.groupOther); comboBoxTargetVersion.setSelectedItem(settings.targetVersion); setEnabledState(settings.pluginEnabled); } @Override public void reset() { loadSettings(); } @Override public void disposeUIResources() { } protected Settings getSettings() { return Settings.getInstance(project); } private void createUIComponents() { // TODO: place custom component creation code here usageLink = SwingHelper.createWebHyperlink(HOW_TO_USE_RT, HOW_TO_USE_LINK); } }