| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.jetbrains.python.configuration; |
| |
| import com.google.common.collect.Lists; |
| import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; |
| import com.intellij.facet.impl.ui.FacetErrorPanel; |
| import com.intellij.facet.ui.FacetConfigurationQuickFix; |
| import com.intellij.facet.ui.FacetEditorValidator; |
| import com.intellij.facet.ui.ValidationResult; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.EditorFactory; |
| import com.intellij.openapi.editor.ex.EditorEx; |
| import com.intellij.openapi.editor.highlighter.EditorHighlighter; |
| import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptor; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; |
| import com.intellij.openapi.fileTypes.PlainTextFileType; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.options.ConfigurationException; |
| import com.intellij.openapi.options.NonDefaultProjectConfigurable; |
| import com.intellij.openapi.options.SearchableConfigurable; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.projectRoots.Sdk; |
| import com.intellij.openapi.roots.ContentIterator; |
| import com.intellij.openapi.roots.ProjectRootManager; |
| import com.intellij.openapi.ui.TextFieldWithBrowseButton; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.ui.CollectionComboBoxModel; |
| import com.intellij.ui.IdeBorderFactory; |
| import com.intellij.util.FileContentUtil; |
| import com.intellij.util.FileContentUtilCore; |
| import com.jetbrains.python.PyBundle; |
| import com.jetbrains.python.PyNames; |
| import com.jetbrains.python.PythonFileType; |
| import com.jetbrains.python.ReSTService; |
| import com.jetbrains.python.documentation.DocStringFormat; |
| import com.jetbrains.python.documentation.PyDocumentationSettings; |
| import com.jetbrains.python.packaging.*; |
| import com.jetbrains.python.sdk.PythonSdkType; |
| import com.jetbrains.python.testing.PythonTestConfigurationsModel; |
| import com.jetbrains.python.testing.TestRunnerService; |
| import com.jetbrains.python.testing.VFSTestFrameworkListener; |
| import org.jetbrains.annotations.Nls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * User: catherine |
| */ |
| public class PyIntegratedToolsConfigurable implements SearchableConfigurable, NonDefaultProjectConfigurable { |
| private JPanel myMainPanel; |
| private JComboBox myTestRunnerComboBox; |
| private JComboBox myDocstringFormatComboBox; |
| private PythonTestConfigurationsModel myModel; |
| @NotNull private final Module myModule; |
| @NotNull private final Project myProject; |
| private final PyDocumentationSettings myDocumentationSettings; |
| private TextFieldWithBrowseButton myWorkDir; |
| private JCheckBox txtIsRst; |
| private JPanel myErrorPanel; |
| private TextFieldWithBrowseButton myRequirementsPathField; |
| private JCheckBox analyzeDoctest; |
| private JPanel myDocStringsPanel; |
| private JPanel myRestPanel; |
| |
| public PyIntegratedToolsConfigurable(@NotNull Module module) { |
| myModule = module; |
| myProject = myModule.getProject(); |
| myDocumentationSettings = PyDocumentationSettings.getInstance(myModule); |
| //noinspection unchecked |
| myDocstringFormatComboBox.setModel(new CollectionComboBoxModel(DocStringFormat.ALL, myDocumentationSettings.myDocStringFormat)); |
| |
| final FileChooserDescriptor fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); |
| myWorkDir.addBrowseFolderListener("Please choose working directory:", null, myProject, fileChooserDescriptor); |
| ReSTService service = ReSTService.getInstance(myModule); |
| myWorkDir.setText(service.getWorkdir()); |
| txtIsRst.setSelected(service.txtIsRst()); |
| analyzeDoctest.setSelected(myDocumentationSettings.analyzeDoctest); |
| myRequirementsPathField.addBrowseFolderListener("Choose path to the package requirements file:", null, myProject, |
| FileChooserDescriptorFactory.createSingleLocalFileDescriptor()); |
| myRequirementsPathField.setText(getRequirementsPath()); |
| |
| myDocStringsPanel.setBorder(IdeBorderFactory.createTitledBorder("Docstrings")); |
| myRestPanel.setBorder(IdeBorderFactory.createTitledBorder("reStructuredText")); |
| } |
| |
| @NotNull |
| private String getRequirementsPath() { |
| final String path = PyPackageRequirementsSettings.getInstance(myModule).getRequirementsPath(); |
| if (path.equals(PyPackageRequirementsSettings.DEFAULT_REQUIREMENTS_PATH) && PyPackageUtil.findRequirementsTxt(myModule) == null) { |
| return ""; |
| } |
| else { |
| return path; |
| } |
| } |
| |
| private void initErrorValidation() { |
| final FacetErrorPanel facetErrorPanel = new FacetErrorPanel(); |
| myErrorPanel.add(facetErrorPanel.getComponent(), BorderLayout.CENTER); |
| |
| facetErrorPanel.getValidatorsManager().registerValidator(new FacetEditorValidator() { |
| @NotNull |
| @Override |
| public ValidationResult check() { |
| final Sdk sdk = PythonSdkType.findPythonSdk(myModule); |
| if (sdk != null) { |
| final Object selectedItem = myTestRunnerComboBox.getSelectedItem(); |
| if (PythonTestConfigurationsModel.PY_TEST_NAME.equals(selectedItem)) { |
| if (!VFSTestFrameworkListener.getInstance().isPyTestInstalled(sdk)) { |
| return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", "py.test"), |
| createQuickFix(sdk, facetErrorPanel, PyNames.PY_TEST)); |
| } |
| } |
| else if (PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME.equals(selectedItem)) { |
| if (!VFSTestFrameworkListener.getInstance().isNoseTestInstalled(sdk)) { |
| return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", "nosetest"), |
| createQuickFix(sdk, facetErrorPanel, PyNames.NOSE_TEST)); |
| } |
| } |
| else if (PythonTestConfigurationsModel.PYTHONS_ATTEST_NAME.equals(selectedItem)) { |
| if (!VFSTestFrameworkListener.getInstance().isAtTestInstalled(sdk)) { |
| return new ValidationResult(PyBundle.message("runcfg.testing.no.test.framework", "attest"), |
| createQuickFix(sdk, facetErrorPanel, PyNames.AT_TEST)); |
| } |
| } |
| } |
| return ValidationResult.OK; |
| } |
| }, myTestRunnerComboBox); |
| |
| facetErrorPanel.getValidatorsManager().validate(); |
| } |
| |
| private FacetConfigurationQuickFix createQuickFix(final Sdk sdk, final FacetErrorPanel facetErrorPanel, final String name) { |
| return new FacetConfigurationQuickFix() { |
| @Override |
| public void run(JComponent place) { |
| final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, sdk, new PyPackageManagerUI.Listener() { |
| @Override |
| public void started() {} |
| |
| @Override |
| public void finished(List<PyExternalProcessException> exceptions) { |
| if (exceptions.isEmpty()) { |
| VFSTestFrameworkListener.getInstance().testInstalled(true, sdk.getHomePath(), name); |
| facetErrorPanel.getValidatorsManager().validate(); |
| } |
| } |
| }); |
| ui.install(Collections.singletonList(new PyRequirement(name)), Collections.<String>emptyList()); |
| } |
| }; |
| } |
| |
| |
| @Nls |
| @Override |
| public String getDisplayName() { |
| return "Python Integrated Tools"; |
| } |
| |
| @Override |
| public String getHelpTopic() { |
| return "test_runner_configuration"; |
| } |
| |
| @Override |
| public JComponent createComponent() { |
| List<String> configurations = TestRunnerService.getInstance(myModule).getConfigurations(); |
| myModel = new PythonTestConfigurationsModel(configurations, |
| TestRunnerService.getInstance(myModule).getProjectConfiguration(), myModule); |
| |
| updateConfigurations(); |
| initErrorValidation(); |
| return myMainPanel; |
| } |
| |
| private void updateConfigurations() { |
| //noinspection unchecked |
| myTestRunnerComboBox.setModel(myModel); |
| } |
| |
| @Override |
| public boolean isModified() { |
| if (myTestRunnerComboBox.getSelectedItem() != myModel.getTestRunner()) { |
| return true; |
| } |
| if (!Comparing.equal(myDocstringFormatComboBox.getSelectedItem(), myDocumentationSettings.myDocStringFormat)) { |
| return true; |
| } |
| if (analyzeDoctest.isSelected() != myDocumentationSettings.analyzeDoctest) { |
| return true; |
| } |
| if (!ReSTService.getInstance(myModule).getWorkdir().equals(myWorkDir.getText())) { |
| return true; |
| } |
| if (!ReSTService.getInstance(myModule).txtIsRst() == txtIsRst.isSelected()) { |
| return true; |
| } |
| if (!getRequirementsPath().equals(myRequirementsPathField.getText())) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void apply() throws ConfigurationException { |
| if (!Comparing.equal(myDocstringFormatComboBox.getSelectedItem(), myDocumentationSettings.myDocStringFormat)) { |
| DaemonCodeAnalyzer.getInstance(myProject).restart(); |
| } |
| if (analyzeDoctest.isSelected() != myDocumentationSettings.analyzeDoctest) { |
| final List<VirtualFile> files = Lists.newArrayList(); |
| ProjectRootManager.getInstance(myProject).getFileIndex().iterateContent(new ContentIterator() { |
| @Override |
| public boolean processFile(VirtualFile fileOrDir) { |
| if (!fileOrDir.isDirectory() && PythonFileType.INSTANCE.getDefaultExtension().equals(fileOrDir.getExtension())) { |
| files.add(fileOrDir); |
| } |
| return true; |
| } |
| }); |
| FileContentUtil.reparseFiles(myProject, Lists.newArrayList(files), false); |
| } |
| myModel.apply(); |
| myDocumentationSettings.myDocStringFormat = (String) myDocstringFormatComboBox.getSelectedItem(); |
| final ReSTService reSTService = ReSTService.getInstance(myModule); |
| reSTService.setWorkdir(myWorkDir.getText()); |
| if (txtIsRst.isSelected() != reSTService.txtIsRst()) { |
| reSTService.setTxtIsRst(txtIsRst.isSelected()); |
| reparseFiles(Collections.singletonList(PlainTextFileType.INSTANCE.getDefaultExtension())); |
| } |
| myDocumentationSettings.analyzeDoctest = analyzeDoctest.isSelected(); |
| PyPackageRequirementsSettings.getInstance(myModule).setRequirementsPath(myRequirementsPathField.getText()); |
| DaemonCodeAnalyzer.getInstance(myProject).restart(); |
| } |
| |
| public void reparseFiles(final List<String> extensions) { |
| final List<VirtualFile> filesToReparse = Lists.newArrayList(); |
| ProjectRootManager.getInstance(myProject).getFileIndex().iterateContent(new ContentIterator() { |
| @Override |
| public boolean processFile(VirtualFile fileOrDir) { |
| if (!fileOrDir.isDirectory() && extensions.contains(fileOrDir.getExtension())) { |
| filesToReparse.add(fileOrDir); |
| } |
| return true; |
| } |
| }); |
| FileContentUtilCore.reparseFiles(filesToReparse); |
| |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| |
| for (Editor editor : EditorFactory.getInstance().getAllEditors()) { |
| if (editor instanceof EditorEx && editor.getProject() == myProject) { |
| final VirtualFile vFile = ((EditorEx)editor).getVirtualFile(); |
| if (vFile != null) { |
| final EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, vFile); |
| ((EditorEx)editor).setHighlighter(highlighter); |
| } |
| } |
| } |
| } |
| }); |
| |
| DaemonCodeAnalyzer.getInstance(myProject).restart(); |
| } |
| |
| @Override |
| public void reset() { |
| myTestRunnerComboBox.setSelectedItem(myModel.getTestRunner()); |
| myTestRunnerComboBox.repaint(); |
| myModel.reset(); |
| myDocstringFormatComboBox.setSelectedItem(myDocumentationSettings.myDocStringFormat); |
| myWorkDir.setText(ReSTService.getInstance(myModule).getWorkdir()); |
| txtIsRst.setSelected(ReSTService.getInstance(myModule).txtIsRst()); |
| analyzeDoctest.setSelected(myDocumentationSettings.analyzeDoctest); |
| myRequirementsPathField.setText(getRequirementsPath()); |
| } |
| |
| @Override |
| public void disposeUIResources() { |
| } |
| |
| @NotNull |
| @Override |
| public String getId() { |
| return "PyIntegratedToolsConfigurable"; |
| } |
| |
| @Override |
| public Runnable enableSearch(String option) { |
| return null; //To change body of implemented methods use File | Settings | File Templates. |
| } |
| } |
| |