diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 865296d135..f3da7263f1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,7 +47,7 @@ sourceSets{ compose.desktop { application { - mainClass = "processing.app.ui.Start" + mainClass = "processing.app.ProcessingKt" jvmArgs(*listOf( Pair("processing.version", rootProject.version), @@ -97,6 +97,7 @@ compose.desktop { dependencies { implementation(project(":core")) + runtimeOnly(project(":java")) implementation(libs.flatlaf) @@ -121,6 +122,8 @@ dependencies { testImplementation(libs.mockitoKotlin) testImplementation(libs.junitJupiter) testImplementation(libs.junitJupiterParams) + + implementation(libs.clikt) } tasks.test { diff --git a/app/src/processing/app/Processing.kt b/app/src/processing/app/Processing.kt new file mode 100644 index 0000000000..11555edf53 --- /dev/null +++ b/app/src/processing/app/Processing.kt @@ -0,0 +1,90 @@ +package processing.app + +import com.github.ajalt.clikt.command.SuspendingCliktCommand +import com.github.ajalt.clikt.command.main +import com.github.ajalt.clikt.core.Context +import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.arguments.help +import com.github.ajalt.clikt.parameters.arguments.multiple +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.help +import com.github.ajalt.clikt.parameters.options.option +import processing.app.ui.Start + +class Processing: SuspendingCliktCommand("processing"){ + val version by option("-v","--version") + .flag() + .help("Print version information") + + val sketches by argument() + .multiple(default = emptyList()) + .help("Sketches to open") + + override fun help(context: Context) = "Start the Processing IDE" + override val invokeWithoutSubcommand = true + override suspend fun run() { + if(version){ + println("processing-${Base.getVersionName()}-${Base.getRevision()}") + return + } + + val subcommand = currentContext.invokedSubcommand + if (subcommand == null) { + Start.main(sketches.toTypedArray()) + } + } +} + +suspend fun main(args: Array){ + Processing() + .subcommands( + LSP(), + LegacyCLI(args) + ) + .main(args) +} + +class LSP: SuspendingCliktCommand("lsp"){ + override fun help(context: Context) = "Start the Processing Language Server" + override suspend fun run(){ + try { + // Indirect invocation since app does not depend on java mode + Class.forName("processing.mode.java.lsp.PdeLanguageServer") + .getMethod("main", Array::class.java) + .invoke(null, *arrayOf(emptyList())) + } catch (e: Exception) { + throw InternalError("Failed to invoke main method", e) + } + } +} + +class LegacyCLI(val args: Array): SuspendingCliktCommand( "cli"){ + override fun help(context: Context) = "Legacy processing-java command line interface" + + val help by option("--help").flag() + val build by option("--build").flag() + val run by option("--run").flag() + val present by option("--present").flag() + val sketch: String? by option("--sketch") + val force by option("--force").flag() + val output: String? by option("--output") + val export by option("--export").flag() + val noJava by option("--no-java").flag() + val variant: String? by option("--variant") + + override suspend fun run(){ + val cliArgs = args.filter { it != "cli" } + try { + if(build){ + System.setProperty("java.awt.headless", "true") + } + // Indirect invocation since app does not depend on java mode + Class.forName("processing.mode.java.Commander") + .getMethod("main", Array::class.java) + .invoke(null, *arrayOf(cliArgs.toTypedArray())) + } catch (e: Exception) { + throw InternalError("Failed to invoke main method", e) + } + } +} \ No newline at end of file diff --git a/app/src/processing/app/tools/InstallCommander.java b/app/src/processing/app/tools/InstallCommander.java index cd136c3621..33eabc6f68 100644 --- a/app/src/processing/app/tools/InstallCommander.java +++ b/app/src/processing/app/tools/InstallCommander.java @@ -86,30 +86,41 @@ public void run() { PrintWriter writer = PApplet.createWriter(file); writer.print("#!/bin/sh\n\n"); - writer.print("# Prevents processing-java from stealing focus, see:\n" + - "# https://github.com/processing/processing/issues/3996.\n" + - "OPTION_FOR_HEADLESS_RUN=\"\"\n" + - "for ARG in \"$@\"\n" + - "do\n" + - " if [ \"$ARG\" = \"--build\" ]; then\n" + - " OPTION_FOR_HEADLESS_RUN=\"-Djava.awt.headless=true\"\n" + - " fi\n" + - "done\n\n"); - - String javaRoot = Platform.getContentFile(".").getCanonicalPath(); - - StringList jarList = new StringList(); - addJarList(jarList, new File(javaRoot)); - addJarList(jarList, new File(javaRoot, "core/library")); - addJarList(jarList, new File(javaRoot, "modes/java/mode")); - String classPath = jarList.join(":").replaceAll(javaRoot + "\\/?", ""); - - writer.println("cd \"" + javaRoot + "\" && " + - Platform.getJavaPath().replaceAll(" ", "\\\\ ") + - " -Djna.nosys=true" + - " $OPTION_FOR_HEADLESS_RUN" + - " -cp \"" + classPath + "\"" + - " processing.mode.java.Commander \"$@\""); + var resourcesDir = System.getProperty("compose.application.resources.dir"); + if(resourcesDir != null) { + // Gradle based distributable + var appBinary = (resourcesDir + .split("\\.app")[0] + ".app/Contents/MacOS/Processing") + .replaceAll(" ", "\\\\ "); + writer.print(appBinary + " cli $@"); + + } else { + // Ant based distributable + writer.print("# Prevents processing-java from stealing focus, see:\n" + + "# https://github.com/processing/processing/issues/3996.\n" + + "OPTION_FOR_HEADLESS_RUN=\"\"\n" + + "for ARG in \"$@\"\n" + + "do\n" + + " if [ \"$ARG\" = \"--build\" ]; then\n" + + " OPTION_FOR_HEADLESS_RUN=\"-Djava.awt.headless=true\"\n" + + " fi\n" + + "done\n\n"); + + String javaRoot = Platform.getContentFile(".").getCanonicalPath(); + + StringList jarList = new StringList(); + addJarList(jarList, new File(javaRoot)); + addJarList(jarList, new File(javaRoot, "core/library")); + addJarList(jarList, new File(javaRoot, "modes/java/mode")); + String classPath = jarList.join(":").replaceAll(javaRoot + "\\/?", ""); + + writer.println("cd \"" + javaRoot + "\" && " + + Platform.getJavaPath().replaceAll(" ", "\\\\ ") + + " -Djna.nosys=true" + + " $OPTION_FOR_HEADLESS_RUN" + + " -cp \"" + classPath + "\"" + + " processing.mode.java.Commander \"$@\""); + } writer.flush(); writer.close(); file.setExecutable(true); diff --git a/core/examples/src/main/java/Basic.java b/core/examples/src/main/java/Basic.java index 379bb4b306..7c5a72cba2 100644 --- a/core/examples/src/main/java/Basic.java +++ b/core/examples/src/main/java/Basic.java @@ -1,12 +1,25 @@ import processing.core.PApplet; +import java.io.IOException; + public class Basic extends PApplet { public void settings(){ size(500, 500); + + try { + Runtime.getRuntime().exec("echo Hello World"); + } catch (IOException e) { + throw new RuntimeException(e); + } } public void draw(){ - ellipse(width / 2f, height / 2f, 125f, 125f); + background(255); + fill(0); + ellipse(mouseX, mouseY, 125f, 125f); + println(frameRate); + + } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a9fe0b6e52..70f93aaff5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,6 +27,7 @@ lsp4j = { module = "org.eclipse.lsp4j:org.eclipse.lsp4j", version = "0.22.0" } jsoup = { module = "org.jsoup:jsoup", version = "1.17.2" } markdown = { module = "com.mikepenz:multiplatform-markdown-renderer-m2", version = "0.31.0" } markdownJVM = { module = "com.mikepenz:multiplatform-markdown-renderer-jvm", version = "0.31.0" } +clikt = { module = "com.github.ajalt.clikt:clikt", version = "5.0.2" } [plugins] jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } diff --git a/java/src/processing/mode/java/lsp/PdeLanguageServer.java b/java/src/processing/mode/java/lsp/PdeLanguageServer.java index 3d865fcc7b..0673b48231 100644 --- a/java/src/processing/mode/java/lsp/PdeLanguageServer.java +++ b/java/src/processing/mode/java/lsp/PdeLanguageServer.java @@ -21,7 +21,7 @@ import org.eclipse.lsp4j.services.LanguageClient; -class PdeLanguageServer implements LanguageServer, LanguageClientAware { +public class PdeLanguageServer implements LanguageServer, LanguageClientAware { Map adapters = new HashMap<>(); LanguageClient client = null; PdeTextDocumentService textDocumentService = new PdeTextDocumentService(this);