package org.elixir_lang.mix.settings; import com.intellij.execution.ExecutionException; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.components.labels.ActionLink; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.download.DownloadableFileDescription; import com.intellij.util.download.DownloadableFileService; import com.intellij.util.download.FileDownloader; import org.elixir_lang.sdk.ElixirSdkRelease; import org.elixir_lang.sdk.ElixirSystemUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.DocumentEvent; import java.awt.*; import java.io.File; import java.util.List; import static org.elixir_lang.sdk.ElixirSystemUtil.transformStdoutLine; /** * Created by zyuyou on 2015/5/26. */ public class MixConfigurationForm { /* * CONSTANTS */ private static final Logger LOGGER = Logger.getInstance(MixConfigurationForm.class); private static final String[][] MIX_ARGUMENTS_ARRAY = new String[][]{ {"--version"}, // Elixir X.Y.Z for mix.bat before 1.2. See https://github.com/elixir-lang/elixir/issues/4075 {"--", "--version"} }; private static final Function<String, String> STDOUT_LINE_TRANSFORMER = new Function<String, String>() { @Override public String fun(String line) { // Elixir X.Y.Z for mix.bat before 1.2 // Mix X.Y.Z for all others if (line.startsWith("Mix")) { return line; } return null; } }; /* * Fields */ private JPanel myPanel; private JTextField myMixVersionText; private JPanel myLinkContainer; private TextFieldWithBrowseButton myMixPathSelector; private JCheckBox supportsFormatterOptionCheckBox; private boolean myMixPathValid; public MixConfigurationForm(){ myMixPathSelector.addBrowseFolderListener("Select Mix executable", "", null, FileChooserDescriptorFactory.createSingleLocalFileDescriptor()); myMixPathSelector.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { @Override protected void textChanged(DocumentEvent documentEvent) { myMixPathValid = validateMixPath(); } }); myMixPathValid = false; } public void setPath(@NotNull String mixPath){ if(!myMixPathSelector.getText().equals(mixPath)){ myMixPathSelector.setText(mixPath); myMixPathValid = validateMixPath(); } } @NotNull public String getPath(){ return myMixPathSelector.getText(); } public boolean getSupportsFormatterOption() { return supportsFormatterOptionCheckBox.isSelected(); } public boolean isPathValid(){ return myMixPathValid; } @Nullable public JComponent createComponent(){ return myPanel; } private boolean validateMixPath(){ String mixPath = myMixPathSelector.getText(); File mix = new File(mixPath); if (!mix.exists()) { return false; } if (!mix.canExecute()) { String reason = mix.getPath() + "is not executable."; LOGGER.warn("Can't detect Mix version: " + reason); return false; } File exeFile = mix.getAbsoluteFile(); String exePath = exeFile.getPath(); String workDir = exeFile.getParent(); ProcessOutput output = null; boolean valid = false; for (String[] arguments : MIX_ARGUMENTS_ARRAY) { try { output = ElixirSystemUtil.getProcessOutput(3000, workDir, exePath, arguments); } catch (ExecutionException executionException) { LOGGER.warn(executionException); } if (output != null) { String transformedStdout = transformStdoutLine(output, STDOUT_LINE_TRANSFORMER); if (transformedStdout != null) { myMixVersionText.setText(transformedStdout); String versionString = transformedStdout.replaceAll("^[^0-9]*", ""); // Support for the --formatter option may be added in a 1.3.x release, but I'm being conservative for now // and assuming it won't be released until 1.4 ElixirSdkRelease elixirSdkRelease = ElixirSdkRelease.fromString(versionString); if (elixirSdkRelease != null) { supportsFormatterOptionCheckBox.setSelected(elixirSdkRelease.compareTo(ElixirSdkRelease.V_1_4) >= 0); } valid = true; break; } else { String stderr = output.getStderr(); StringBuilder text = new StringBuilder("N/A"); if (StringUtil.isNotEmpty(stderr)) { text.append(": Error: ").append(stderr); } myMixVersionText.setText(text.toString()); } } } return valid; } private void createUIComponents(){ myLinkContainer = new JPanel(new BorderLayout()); ActionLink link = new ActionLink("Download the latest Mix version", new AnAction() { @Override public void actionPerformed(AnActionEvent anActionEvent) { DownloadableFileService service = DownloadableFileService.getInstance(); DownloadableFileDescription mix = service.createFileDescription("http://s3.hex.pm/builds/mix/mix", "mix"); FileDownloader downloader = service.createDownloader(ContainerUtil.list(mix), "mix"); List<Pair<VirtualFile, DownloadableFileDescription>> pairs = downloader.downloadWithProgress(null, getEventProject(anActionEvent), myLinkContainer); if(pairs != null){ for (Pair<VirtualFile, DownloadableFileDescription> pair : pairs){ try { String path = pair.first.getCanonicalPath(); if(path != null){ FileUtilRt.setExecutableAttribute(path, true); myMixPathSelector.setText(path); validateMixPath(); } }catch (Exception e){ // Ignore } } } } }); myLinkContainer.add(link, BorderLayout.NORTH); } }