From fd571d13d5e12de0ca196a13c5890cdd787ba8b5 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Fri, 18 Apr 2025 17:42:31 +0200 Subject: [PATCH 1/4] Initial Command Line structure --- app/build.gradle.kts | 5 +- app/src/processing/app/Processing.kt | 70 +++++++++++++++++++ core/examples/src/main/java/Basic.java | 15 +++- .../mode/java/lsp/PdeLanguageServer.java | 2 +- 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 app/src/processing/app/Processing.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 865296d135..f02a511ccd 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("com.github.ajalt.clikt:clikt:5.0.2") } tasks.test { diff --git a/app/src/processing/app/Processing.kt b/app/src/processing/app/Processing.kt new file mode 100644 index 0000000000..82f89572dd --- /dev/null +++ b/app/src/processing/app/Processing.kt @@ -0,0 +1,70 @@ +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.options.flag +import com.github.ajalt.clikt.parameters.options.option +import processing.app.ui.Start + +// TODO: Allow Start to run on no args +// TODO: Modify InstallCommander to use the new structure +// TODO: Move dependency to gradle toml +// TODO: Add the options/arguments for Base arguments +class Processing(val args: Array): SuspendingCliktCommand(name = "Processing"){ + override suspend fun run() { + if(currentContext.invokedSubcommand == null){ + Start.main(args) + } + } +} + +suspend fun main(args: Array) = Processing(args) + .subcommands( + LSP(args), + LegacyCLI(args) + ) + .main(args) + + +class LSP(val args: Array): 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(args)) + } catch (e: Exception) { + throw InternalError("Failed to invoke main method", e) + } + } +} + +class LegacyCLI(val args: Array): SuspendingCliktCommand(name = "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" }.toTypedArray() + try { + // 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)) + } catch (e: Exception) { + throw InternalError("Failed to invoke main method", e) + } + } +} \ No newline at end of file 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/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); From 51c9734d2ae28fa4f86f4638db39ba701d079cea Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Sat, 19 Apr 2025 09:34:06 +0200 Subject: [PATCH 2/4] Refined Command Line Structure --- app/build.gradle.kts | 2 +- app/src/processing/app/Processing.kt | 45 ++++++++------ .../app/tools/InstallCommander.java | 59 +++++++++++-------- gradle/libs.versions.toml | 1 + 4 files changed, 63 insertions(+), 44 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f02a511ccd..f3da7263f1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -123,7 +123,7 @@ dependencies { testImplementation(libs.junitJupiter) testImplementation(libs.junitJupiterParams) - implementation("com.github.ajalt.clikt:clikt:5.0.2") + implementation(libs.clikt) } tasks.test { diff --git a/app/src/processing/app/Processing.kt b/app/src/processing/app/Processing.kt index 82f89572dd..498d63b210 100644 --- a/app/src/processing/app/Processing.kt +++ b/app/src/processing/app/Processing.kt @@ -4,45 +4,49 @@ 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.multiple import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option import processing.app.ui.Start -// TODO: Allow Start to run on no args -// TODO: Modify InstallCommander to use the new structure -// TODO: Move dependency to gradle toml -// TODO: Add the options/arguments for Base arguments -class Processing(val args: Array): SuspendingCliktCommand(name = "Processing"){ +class Processing: SuspendingCliktCommand("processing"){ + val sketches by argument().multiple(default = emptyList()) + + override fun help(context: Context) = "Start the Processing IDE" + override val invokeWithoutSubcommand = true override suspend fun run() { - if(currentContext.invokedSubcommand == null){ - Start.main(args) + val subcommand = currentContext.invokedSubcommand + if (subcommand == null) { + Start.main(sketches.toTypedArray()) } } } -suspend fun main(args: Array) = Processing(args) - .subcommands( - LSP(args), - LegacyCLI(args) - ) - .main(args) - +suspend fun main(args: Array){ + Processing() + .subcommands( + LSP(), + LegacyCLI(args) + ) + .main(args) +} -class LSP(val args: Array): SuspendingCliktCommand("lsp"){ +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(args)) + .invoke(null, *arrayOf(emptyList())) } catch (e: Exception) { throw InternalError("Failed to invoke main method", e) } } } -class LegacyCLI(val args: Array): SuspendingCliktCommand(name = "cli"){ +class LegacyCLI(val args: Array): SuspendingCliktCommand( "cli"){ override fun help(context: Context) = "Legacy processing-java command line interface" val help by option("--help").flag() @@ -57,12 +61,15 @@ class LegacyCLI(val args: Array): SuspendingCliktCommand(name = "cli"){ val variant: String? by option("--variant") override suspend fun run(){ - val cliArgs = args.filter { it != "cli" }.toTypedArray() + 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)) + .invoke(null, *arrayOf(cliArgs.toTypedArray())) } catch (e: Exception) { throw InternalError("Failed to invoke main method", e) } 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/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" } From 30fd531a446c661809345fe1d487a0fbf701528b Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Sat, 19 Apr 2025 09:54:15 +0200 Subject: [PATCH 3/4] Improving documentation --- app/src/processing/app/Processing.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/processing/app/Processing.kt b/app/src/processing/app/Processing.kt index 498d63b210..9e7324dd3c 100644 --- a/app/src/processing/app/Processing.kt +++ b/app/src/processing/app/Processing.kt @@ -5,13 +5,16 @@ 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.option import processing.app.ui.Start class Processing: SuspendingCliktCommand("processing"){ - val sketches by argument().multiple(default = emptyList()) + val sketches by argument() + .multiple(default = emptyList()) + .help("Sketches to open") override fun help(context: Context) = "Start the Processing IDE" override val invokeWithoutSubcommand = true From 036d01e90dae7711523715a45a2195c7b6043ad6 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Sat, 19 Apr 2025 12:28:24 +0200 Subject: [PATCH 4/4] Added version option --- app/src/processing/app/Processing.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/processing/app/Processing.kt b/app/src/processing/app/Processing.kt index 9e7324dd3c..11555edf53 100644 --- a/app/src/processing/app/Processing.kt +++ b/app/src/processing/app/Processing.kt @@ -8,10 +8,15 @@ 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") @@ -19,6 +24,11 @@ class Processing: SuspendingCliktCommand("processing"){ 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())