package com.jscs.settings; import com.jscs.JscsProjectComponent; import com.jscs.cli.JscsFinder; import com.jscs.cli.JscsRunner; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.execution.ExecutionException; 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.jscs.cli.JscsSettings; import com.wix.settings.ValidationInfo; import com.wix.settings.ValidationUtils; import com.wix.ui.PackagesNotificationPanel; import com.wix.utils.FileUtils; import org.apache.commons.lang.StringUtils; 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.ArrayList; import java.util.List; public class JscsSettingsPage implements Configurable { public static final String FIX_IT = "Fix it"; public static final String HOW_TO_USE_JSCS = "How to Use JSCS"; public static final String HOW_TO_USE_LINK = "https://github.com/idok/jscs-plugin"; protected Project project; private JCheckBox pluginEnabledCheckbox; private JTextField presetField; private JPanel panel; private JPanel errorPanel; private TextFieldWithHistoryWithBrowseButton jscsBinField; private TextFieldWithHistoryWithBrowseButton nodeInterpreterField; private TextFieldWithHistoryWithBrowseButton jscsrcFile; private JRadioButton searchForJscsrcInRadioButton; private JRadioButton useProjectJscsrcRadioButton; private HyperlinkLabel usageLink; private JLabel jscsConfigFilePathLabel; private JLabel rulesDirectoryLabel; private JLabel pathToJscsBinLabel; private JLabel nodeInterpreterLabel; private JCheckBox treatAllIssuesCheckBox; private JLabel versionLabel; private JCheckBox esnextCheckBox; private JTextField esprimaField; private final PackagesNotificationPanel packagesNotificationPanel; public JscsSettingsPage(@NotNull final Project project) { this.project = project; configBinField(); configJscsRcField(); configNodeField(); useProjectJscsrcRadioButton.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { jscsrcFile.setEnabled(e.getStateChange() == ItemEvent.SELECTED); } }); pluginEnabledCheckbox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { boolean enabled = e.getStateChange() == ItemEvent.SELECTED; setEnabledState(enabled); } }); this.packagesNotificationPanel = new PackagesNotificationPanel(project); errorPanel.add(this.packagesNotificationPanel.getComponent(), BorderLayout.CENTER); DocumentAdapter docAdp = new DocumentAdapter() { protected void textChanged(DocumentEvent e) { updateLaterInEDT(); } }; jscsBinField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); jscsrcFile.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); nodeInterpreterField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); // rulesPathField.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); presetField.getDocument().addDocumentListener(docAdp); } private void addDocumentListenerToComp(TextFieldWithHistoryWithBrowseButton field, DocumentAdapter docAdp) { field.getChildComponent().getTextEditor().getDocument().addDocumentListener(docAdp); } private File getProjectPath() { if (project == null || project.getBaseDir() == null) { return null; } return new File(project.getBaseDir().getPath()); } private void updateLaterInEDT() { UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { JscsSettingsPage.this.update(); } }); } private void update() { ApplicationManager.getApplication().assertIsDispatchThread(); validate(); } private void setEnabledState(boolean enabled) { jscsrcFile.setEnabled(enabled); presetField.setEnabled(enabled); searchForJscsrcInRadioButton.setEnabled(enabled); useProjectJscsrcRadioButton.setEnabled(enabled); jscsBinField.setEnabled(enabled); nodeInterpreterField.setEnabled(enabled); jscsConfigFilePathLabel.setEnabled(enabled); rulesDirectoryLabel.setEnabled(enabled); pathToJscsBinLabel.setEnabled(enabled); nodeInterpreterLabel.setEnabled(enabled); treatAllIssuesCheckBox.setEnabled(enabled); esnextCheckBox.setEnabled(enabled); esprimaField.setEnabled(enabled); } private void validateField(List<ValidationInfo> errors, TextFieldWithHistoryWithBrowseButton field, boolean allowEmpty, String message) { if (!ValidationUtils.validatePath(project, field.getChildComponent().getText(), allowEmpty)) { ValidationInfo error = new ValidationInfo(field.getChildComponent().getTextEditor(), message, FIX_IT); errors.add(error); } } private void validate() { if (!pluginEnabledCheckbox.isSelected()) { return; } List<ValidationInfo> errors = new ArrayList<ValidationInfo>(); validateField(errors, jscsBinField, false, "Path to jscs is invalid {{LINK}}"); validateField(errors, jscsrcFile, true, "Path to jscsrc is invalid {{LINK}}"); //Please correct path to validateField(errors, nodeInterpreterField, false, "Path to node interpreter is invalid {{LINK}}"); if (errors.isEmpty()) { try { packagesNotificationPanel.removeAllLinkHandlers(); } catch (Exception e) { e.printStackTrace(); } packagesNotificationPanel.hide(); getVersion(); } else { packagesNotificationPanel.showErrors(errors); } } private JscsSettings settings; private void getVersion() { if (settings != null && areEqual(nodeInterpreterField, settings.node) && areEqual(jscsBinField, settings.jscsExecutablePath) && settings.cwd.equals(project.getBasePath()) ) { return; } settings = new JscsSettings(); settings.node = nodeInterpreterField.getChildComponent().getText(); settings.jscsExecutablePath = jscsBinField.getChildComponent().getText(); settings.cwd = project.getBasePath(); try { String version = JscsRunner.version(settings); versionLabel.setText(version.trim()); } catch (ExecutionException e) { e.printStackTrace(); } } private static TextFieldWithHistory configWithDefaults(TextFieldWithHistoryWithBrowseButton field) { TextFieldWithHistory textFieldWithHistory = field.getChildComponent(); textFieldWithHistory.setHistorySize(-1); textFieldWithHistory.setMinimumAndPreferredWidth(0); return textFieldWithHistory; } private void configBinField() { configWithDefaults(jscsBinField); SwingHelper.addHistoryOnExpansion(jscsBinField.getChildComponent(), new NotNullProducer<List<String>>() { @NotNull public List<String> produce() { List<File> newFiles = JscsFinder.searchForJscsBin(getProjectPath()); return FileUtils.toAbsolutePath(newFiles); } }); SwingHelper.installFileCompletionAndBrowseDialog(project, jscsBinField, "Select jscs.js cli", FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()); } private void configJscsRcField() { TextFieldWithHistory textFieldWithHistory = configWithDefaults(jscsrcFile); SwingHelper.addHistoryOnExpansion(textFieldWithHistory, new NotNullProducer<List<String>>() { @NotNull public List<String> produce() { return JscsFinder.searchForJscsRCFiles(getProjectPath()); } }); SwingHelper.installFileCompletionAndBrowseDialog(project, jscsrcFile, "Select JSCS config", 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 "JSCS"; } @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); } @Override public boolean isModified() { Settings s = getSettings(); Settings uiSettings = toSettings(); return !s.isEqualTo(uiSettings); } private String getJscsRCFile() { return useProjectJscsrcRadioButton.isSelected() ? jscsrcFile.getChildComponent().getText() : ""; } @Override public void apply() throws ConfigurationException { saveSettings(); PsiManager.getInstance(project).dropResolveCaches(); } protected void saveSettings() { Settings settings = getSettings(); copyTo(settings); project.getComponent(JscsProjectComponent.class).validateSettings(); DaemonCodeAnalyzer.getInstance(project).restart(); } public Settings toSettings() { Settings settings = new Settings(); copyTo(settings); return settings; } public void copyTo(Settings settings) { settings.pluginEnabled = pluginEnabledCheckbox.isSelected(); settings.jscsExecutable = jscsBinField.getChildComponent().getText(); settings.nodeInterpreter = nodeInterpreterField.getChildComponent().getText(); settings.jscsrcFile = getJscsRCFile(); settings.preset = presetField.getText(); settings.esprima = esprimaField.getText(); settings.esnext = esnextCheckBox.isSelected(); settings.treatAllIssuesAsWarnings = treatAllIssuesCheckBox.isSelected(); } protected void loadSettings() { Settings settings = getSettings(); pluginEnabledCheckbox.setSelected(settings.pluginEnabled); jscsBinField.getChildComponent().setText(settings.jscsExecutable); jscsrcFile.getChildComponent().setText(settings.jscsrcFile); nodeInterpreterField.getChildComponent().setText(settings.nodeInterpreter); presetField.setText(settings.preset); esprimaField.setText(settings.esprima); useProjectJscsrcRadioButton.setSelected(StringUtils.isNotEmpty(settings.jscsrcFile)); searchForJscsrcInRadioButton.setSelected(StringUtils.isEmpty(settings.jscsrcFile)); jscsrcFile.setEnabled(useProjectJscsrcRadioButton.isSelected()); treatAllIssuesCheckBox.setSelected(settings.treatAllIssuesAsWarnings); esnextCheckBox.setSelected(settings.esnext); 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_JSCS, HOW_TO_USE_LINK); } }