From 0248ad6ef2ca4ddd9e2dc49bb50e7b9a849fa417 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Thu, 19 Oct 2023 15:58:14 +1100 Subject: [PATCH] Working automatic `composer require` on add ss ref. Fixes for update reference. --- .../UpdateServiceStackReferenceIntention.java | 11 +- .../idea/UpdateServiceStackUtils.java | 38 +++--- .../idea/php/AddPhpRefHandler.java | 55 ++++----- .../idea/php/ComposerPackageManager.java | 74 +++++++++++ .../idea/php/ComposerProgressIndicator.java | 116 ++++++++++++++++++ 5 files changed, 248 insertions(+), 46 deletions(-) create mode 100644 src/main/java/net/servicestack/idea/php/ComposerPackageManager.java create mode 100644 src/main/java/net/servicestack/idea/php/ComposerProgressIndicator.java diff --git a/src/main/java/net/servicestack/idea/UpdateServiceStackReferenceIntention.java b/src/main/java/net/servicestack/idea/UpdateServiceStackReferenceIntention.java index 556a94d..871786c 100644 --- a/src/main/java/net/servicestack/idea/UpdateServiceStackReferenceIntention.java +++ b/src/main/java/net/servicestack/idea/UpdateServiceStackReferenceIntention.java @@ -1,6 +1,7 @@ package net.servicestack.idea; import com.intellij.codeInsight.intention.impl.QuickEditAction; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; @@ -49,7 +50,15 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile psiF @Override public void invoke(@NotNull Project project, Editor editor, final PsiFile psiFile) throws IncorrectOperationException { - UpdateServiceStackUtils.updateServiceStackReference(psiFile); + // First check if on write thread + if (ApplicationManager.getApplication().isWriteAccessAllowed()) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + UpdateServiceStackUtils.updateServiceStackReference(psiFile); + } + }); + } } @Override diff --git a/src/main/java/net/servicestack/idea/UpdateServiceStackUtils.java b/src/main/java/net/servicestack/idea/UpdateServiceStackUtils.java index a85915f..70293ea 100644 --- a/src/main/java/net/servicestack/idea/UpdateServiceStackUtils.java +++ b/src/main/java/net/servicestack/idea/UpdateServiceStackUtils.java @@ -10,8 +10,10 @@ import net.servicestack.idea.common.Analytics; import net.servicestack.idea.common.INativeTypesHandler; import org.apache.http.client.utils.URIBuilder; +import org.jetbrains.annotations.NotNull; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStreamReader; import java.net.URISyntaxException; import java.net.URL; @@ -102,21 +104,7 @@ public static void updateServiceStackReference(PsiFile psiFile) { serverUrl.append(option.getKey()).append("=").append(option.getValue().trim().replaceAll("\\u0020", "")); count++; } - URL javaCodeUrl = new URL(serverUrl.toString()); - - URLConnection javaCodeConnection = javaCodeUrl.openConnection(); - javaCodeConnection.setRequestProperty("content-type", "application/json; charset=utf-8"); - BufferedReader javaCodeBufferReader = new BufferedReader( - new InputStreamReader( - javaCodeConnection.getInputStream())); - String javaCodeInput; - StringBuilder javaCodeResponse = new StringBuilder(); - while ((javaCodeInput = javaCodeBufferReader.readLine()) != null) { - javaCodeResponse.append(javaCodeInput); - //All documents inside IntelliJ IDEA always use \n line separators. - //http://confluence.jetbrains.net/display/IDEADEV/IntelliJ+IDEA+Architectural+Overview - javaCodeResponse.append("\n"); - } + StringBuilder javaCodeResponse = getJavaCodeResponse(serverUrl); String javaCode = javaCodeResponse.toString(); if (!javaCode.startsWith(nativeTypesHandler.getOptionsCommentStart())) { @@ -143,6 +131,26 @@ public static void updateServiceStackReference(PsiFile psiFile) { } } + @NotNull + private static StringBuilder getJavaCodeResponse(StringBuilder serverUrl) throws IOException { + URL javaCodeUrl = new URL(serverUrl.toString()); + + URLConnection javaCodeConnection = javaCodeUrl.openConnection(); + javaCodeConnection.setRequestProperty("content-type", "application/json; charset=utf-8"); + BufferedReader javaCodeBufferReader = new BufferedReader( + new InputStreamReader( + javaCodeConnection.getInputStream())); + String javaCodeInput; + StringBuilder javaCodeResponse = new StringBuilder(); + while ((javaCodeInput = javaCodeBufferReader.readLine()) != null) { + javaCodeResponse.append(javaCodeInput); + //All documents inside IntelliJ IDEA always use \n line separators. + //http://confluence.jetbrains.net/display/IDEADEV/IntelliJ+IDEA+Architectural+Overview + javaCodeResponse.append("\n"); + } + return javaCodeResponse; + } + public static String combinePath(String path, String segment) { if (path == null || path.isEmpty()) { return "/" + segment; diff --git a/src/main/java/net/servicestack/idea/php/AddPhpRefHandler.java b/src/main/java/net/servicestack/idea/php/AddPhpRefHandler.java index c8340fe..7fdc07a 100644 --- a/src/main/java/net/servicestack/idea/php/AddPhpRefHandler.java +++ b/src/main/java/net/servicestack/idea/php/AddPhpRefHandler.java @@ -1,9 +1,15 @@ package net.servicestack.idea.php; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.jetbrains.php.composer.ComposerDataService; +import com.jetbrains.php.composer.addDependency.ComposerPackage; import net.minidev.json.JSONObject; import net.minidev.json.JSONValue; import net.minidev.json.parser.ParseException; @@ -11,6 +17,7 @@ import net.servicestack.idea.common.DialogErrorMessages; import net.servicestack.idea.common.IDEAUtils; import net.servicestack.idea.common.INativeTypesHandler; +import com.jetbrains.php.composer.actions.ComposerInstallAction; import java.io.File; import java.io.FileNotFoundException; @@ -76,7 +83,8 @@ private static void tryUpdateComposerJson(Module module, StringBuilder errorMess // This is very inconsistent and doesn't always work // Editing the `composer.json` manually causes errors from `composer`. // Leaving this out until I can figure out a better way to do this. - // installPackage(module); + installPackage(module); + } catch (IOException | ParseException e) { errorMessage.append(e.getMessage()); e.printStackTrace(); @@ -85,35 +93,22 @@ private static void tryUpdateComposerJson(Module module, StringBuilder errorMess } } -// public static void installPackage(Module module) { -// // Retrieve the module's root manager -// ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); -// -// // Retrieve the module's content roots -// VirtualFile[] roots = moduleRootManager.getContentRoots(); -// -// if (roots.length == 0) { -// Logger.getInstance(AddPhpRefHandler.class).warn("No content roots found"); -// return; -// } -// -// VirtualFile root = roots[0]; // Assuming first content root is the main project root -// String projectBasePath = root.getPath(); -// -// ApplicationManager.getApplication().executeOnPooledThread(() -> { -// GeneralCommandLine commandLine = new GeneralCommandLine(); -// commandLine.withWorkDirectory(projectBasePath); -// commandLine.setExePath("composer"); -// commandLine.addParameter("require"); -// commandLine.addParameter("servicestack/client"); -// -// try { -// commandLine.createProcess(); -// } catch (Exception e) { -// Logger.getInstance(AddPhpRefHandler.class).error(e); -// } -// }); -// } + public static void installPackage(Module module) { + Project project = module.getProject(); + ComposerDataService composerDataService = project.getService(ComposerDataService.class); + VirtualFile configFile = composerDataService.getConfigFile(); + if(configFile == null) { + return; + } + ComposerPackageManager composerPackageManager = new ComposerPackageManager(project); + ComposerPackageManager.DependentPackage dependentPackage = ComposerPackageManager.DependentPackage.SERVICESTACK_CLIENT; + ComposerPackage composerPackage = composerPackageManager.findPackage(dependentPackage); + if (composerPackage == null) { + composerPackageManager.installPackage(dependentPackage, configFile); + } + } + + private static List getDtoLines(String addressUrl, INativeTypesHandler nativeTypesHandler, StringBuilder errorMessage) { diff --git a/src/main/java/net/servicestack/idea/php/ComposerPackageManager.java b/src/main/java/net/servicestack/idea/php/ComposerPackageManager.java new file mode 100644 index 0000000..b9a4bfe --- /dev/null +++ b/src/main/java/net/servicestack/idea/php/ComposerPackageManager.java @@ -0,0 +1,74 @@ +package net.servicestack.idea.php; + +import com.intellij.execution.process.CapturingProcessAdapter; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.php.composer.ComposerDataService; +import com.jetbrains.php.composer.ComposerUtils; +import com.jetbrains.php.composer.actions.ComposerCommandExecutor; +import com.jetbrains.php.composer.actions.ComposerCommandRunner; +import com.jetbrains.php.composer.actions.ComposerUpdateAction; +import com.jetbrains.php.composer.addDependency.ComposerPackage; +import com.jetbrains.php.composer.addDependency.ComposerPackagesUtil; +import com.jetbrains.php.composer.execution.ComposerExecution; +import com.jetbrains.php.composer.statistics.ComposerActionStatistics; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +public class ComposerPackageManager { + + private final Project project; + + public ComposerPackageManager(Project project) { + this.project = project; + } + + public ComposerPackage findPackage(DependentPackage dependentPackage) { + ComposerExecution execution = ComposerDataService.getInstance(project).getComposerExecution(); + ComposerCommandRunner executor = new ComposerCommandRunner(execution, project, project.getBasePath(), new ComposerProgressIndicator()); + CapturingProcessAdapter outputCapturingAdapter = new CapturingProcessAdapter(); + List command = Arrays.asList("show", dependentPackage.getName(), "--no-ansi", "-d", project.getBasePath()); + ComposerCommandRunner.ExecutionResult executionResult = executor.runCommand(command, outputCapturingAdapter); + if (executionResult.myProgressIndicatorCancelled || !executionResult.isSuccess()) { + return null; + } + + try { + return ComposerPackagesUtil.parsePackageDescriptionCommandOutput(dependentPackage.getName(), outputCapturingAdapter.getOutput().getStdout()); + } catch (IOException e) { + return null; + } + } + + public void installPackage(DependentPackage dependentPackage, VirtualFile configFile) { + ComposerCommandExecutor commandExecutor = ComposerUpdateAction.createExecutor( + project, + configFile, + "--no-interaction --no-ansi -d " + project.getBasePath(), + "", + ComposerActionStatistics.Action.REQUIRE, + ComposerUtils.getInstallationCommand(dependentPackage.getName(), dependentPackage.getVersion())); + commandExecutor.execute(); + } + + public enum DependentPackage { + SERVICESTACK_CLIENT("servicestack/client", ""); + private final String name; + private final String version; + + DependentPackage(String name, String version) { + this.name = name; + this.version = version; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + } +} diff --git a/src/main/java/net/servicestack/idea/php/ComposerProgressIndicator.java b/src/main/java/net/servicestack/idea/php/ComposerProgressIndicator.java new file mode 100644 index 0000000..dd148bf --- /dev/null +++ b/src/main/java/net/servicestack/idea/php/ComposerProgressIndicator.java @@ -0,0 +1,116 @@ +package net.servicestack.idea.php; + +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.util.NlsContexts; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ComposerProgressIndicator implements ProgressIndicator { + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public boolean isRunning() { + return false; + } + + @Override + public void cancel() { + + } + + @Override + public boolean isCanceled() { + return false; + } + + + @Override + public void setText(@NlsContexts.ProgressText String text) { + + } + + @Override + public @NlsContexts.ProgressText String getText() { + return null; + } + + @Override + public void setText2(@NlsContexts.ProgressDetails String text) { + + } + + @Override + public @NlsContexts.ProgressDetails String getText2() { + return null; + } + + @Override + public double getFraction() { + return 0; + } + + @Override + public void setFraction(double fraction) { + + } + + @Override + public void pushState() { + + } + + @Override + public void popState() { + + } + + @Override + public boolean isModal() { + return false; + } + + @Override + public @NotNull ModalityState getModalityState() { + return null; + } + + @Override + public void setModalityProgress(@Nullable ProgressIndicator modalityProgress) { + + } + + @Override + public boolean isIndeterminate() { + return false; + } + + @Override + public void setIndeterminate(boolean indeterminate) { + + } + + @Override + public void checkCanceled() throws ProcessCanceledException { + + } + + @Override + public boolean isPopupWasShown() { + return false; + } + + @Override + public boolean isShowing() { + return false; + } +}