This is an automated email from the ASF dual-hosted git repository. sunlan pushed a commit to branch GROOVY-9468 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 0e0d10a9c0bcdce8ec66f4c06d9cbfece4115c18 Author: Daniel Sun <[email protected]> AuthorDate: Sun Mar 15 02:50:45 2020 +0800 GROOVY-9468: Support running as Java code in the Groovy console --- subprojects/groovy-console/build.gradle | 1 + .../main/groovy/groovy/console/ui/Console.groovy | 86 +++++++++++++++++++--- .../groovy/groovy/console/ui/ConsoleActions.groovy | 13 ++++ .../groovy/console/ui/view/BasicMenuBar.groovy | 2 + .../groovy/console/ui/view/MacOSXMenuBar.groovy | 2 + 5 files changed, 95 insertions(+), 9 deletions(-) diff --git a/subprojects/groovy-console/build.gradle b/subprojects/groovy-console/build.gradle index 4b48627..e29322e 100644 --- a/subprojects/groovy-console/build.gradle +++ b/subprojects/groovy-console/build.gradle @@ -20,6 +20,7 @@ evaluationDependsOn(':groovy-swing') dependencies { api rootProject // AstBrowser has methods with Closure params... + implementation "com.github.javaparser:javaparser-core:$javaParserVersion" implementation project(':groovy-swing') implementation project(':groovy-templates') testImplementation project(':groovy-test') diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy index 6117100..9617d39 100644 --- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy +++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy @@ -18,6 +18,11 @@ */ package groovy.console.ui + +import com.github.javaparser.StaticJavaParser +import com.github.javaparser.ast.CompilationUnit +import com.github.javaparser.ast.Modifier +import com.github.javaparser.ast.body.TypeDeclaration import groovy.cli.internal.CliBuilderInternal import groovy.cli.internal.OptionAccessor import groovy.console.ui.text.FindReplaceUtility @@ -35,6 +40,7 @@ import org.apache.groovy.antlr.LexerFrame import org.apache.groovy.io.StringBuilderWriter import org.apache.groovy.parser.antlr4.GroovyLangLexer import org.apache.groovy.parser.antlr4.GroovyLangParser +import org.apache.groovy.util.JavaShell import org.apache.groovy.util.SystemUtil import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.control.ErrorCollector @@ -1136,13 +1142,25 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo return consoleControllers.find { it.consoleId == consoleId } } + private enum SourceType { + GROOVY, JAVA + } + + void runJava(EventObject evt = null) { + doRun(evt, SourceType.JAVA) + } + // actually run the script void runScript(EventObject evt = null) { + doRun(evt, SourceType.GROOVY) + } + + private doRun(EventObject evt = null, SourceType st) { saveInputAreaContentHash() if (saveOnRun && scriptFile != null) { - if (fileSave(evt)) runScriptImpl(false) + if (fileSave(evt)) runScriptImpl(false, st) } else { - runScriptImpl(false) + runScriptImpl(false, st) } } @@ -1180,11 +1198,20 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo cc.getOptimizationOptions().get(CompilerConfiguration.INVOKEDYNAMIC) } + void runSelectedJava(EventObject evt = null) { + doRunSelected(evt, SourceType.JAVA) + } + void runSelectedScript(EventObject evt = null) { + doRunSelected(evt, SourceType.GROOVY) + } + + private void doRunSelected(EventObject evt = null, SourceType st) { saveInputAreaContentHash() - runScriptImpl(true) + runScriptImpl(true, st) } + void addClasspathJar(EventObject evt = null) { def fc = new JFileChooser(currentClasspathJarDir) fc.fileSelectionMode = JFileChooser.FILES_ONLY @@ -1251,7 +1278,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo inputAreaContentHash = inputArea.getText().hashCode() } - private void runScriptImpl(boolean selected) { + private void runScriptImpl(boolean selected, SourceType st = SourceType.GROOVY) { if (scriptRunning) { statusLabel.text = 'Cannot run script now as a script is already running. Please wait or use "Interrupt Script" option.' return @@ -1269,8 +1296,9 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo // Print the input text if (showScriptInOutput) { + final promptPrfix = SourceType.JAVA === st ? 'java> ' : 'groovy> ' for (line in record.getTextToRun(selected).tokenize('\n')) { - appendOutputNl('groovy> ', promptStyle) + appendOutputNl(promptPrfix, promptStyle) appendOutput(line, commandStyle) } appendOutputNl(' \n', promptStyle) @@ -1282,7 +1310,6 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo try { systemOutInterceptor.setConsoleId(this.getConsoleId()) SwingUtilities.invokeLater { showExecutingMessage() } - String name = scriptFile?.name ?: (DEFAULT_SCRIPT_NAME_START + scriptNameCounter++) if (beforeExecution) { beforeExecution() } @@ -1291,13 +1318,13 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo ClassLoader savedThreadContextClassLoader = Thread.currentThread().contextClassLoader try { Thread.currentThread().contextClassLoader = shell.classLoader - result = shell.run(record.getTextToRun(selected), name, []) + result = doRun(selected, st, record) } finally { Thread.currentThread().contextClassLoader = savedThreadContextClassLoader } } else { - result = shell.run(record.getTextToRun(selected), name, []) + result = doRun(selected, st, record) } if (afterExecution) { afterExecution() @@ -1319,7 +1346,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo int delay = prefs.getInt('loopModeDelay', ConsolePreferences.DEFAULT_LOOP_MODE_DELAY_MILLIS) Timer timer = new Timer(delay, { if( inputAreaContentHash == inputArea.getText().hashCode() ) { - runScriptImpl(selected) + runScriptImpl(selected, st) } }) timer.repeats = false @@ -1329,6 +1356,47 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo } } + @CompileStatic + private Object doRun(boolean selected, SourceType st, HistoryRecord record) { + def result + def src = record.getTextToRun(selected) + if (SourceType.JAVA === st) { + Optional<String> optionalPrimaryClassName = findPrimaryClassName(src) + if (optionalPrimaryClassName.isPresent()) { + def js = new JavaShell(Thread.currentThread().contextClassLoader) + js.run(optionalPrimaryClassName.get(), src) + } + } else { + String name = ((File) scriptFile)?.name ?: (DEFAULT_SCRIPT_NAME_START + scriptNameCounter++) + result = shell.run(src, name, []) + } + return result + } + + @CompileStatic + private static Optional<String> findPrimaryClassName(String javaSrc) { + List<TypeDeclaration<?>> result = new LinkedList<>() + CompilationUnit compilationUnit = StaticJavaParser.parse(javaSrc) + + for (TypeDeclaration<?> td : compilationUnit.getTypes()) { + if (!(td.isTopLevelType() && td.isClassOrInterfaceDeclaration() && td.getModifiers().contains(Modifier.publicModifier()))) continue + + if (td.fullyQualifiedName.isPresent()) { + result << td + } + } + + String className = null + if (!result.isEmpty()) { + Optional<String> optionalClassName = result.get(0).getFullyQualifiedName() + if (optionalClassName.isPresent()) { + className = optionalClassName.get() + } + } + + return Optional.ofNullable(className) + } + void compileScript(EventObject evt = null) { if (scriptRunning) { statusLabel.text = 'Cannot compile script now as a script is already running. Please wait or use "Interrupt Script" option.' diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy index 57a3f19..fb05c2c 100644 --- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy +++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy @@ -204,6 +204,14 @@ runAction = action( shortDescription: 'Execute Groovy Script' ) +runJavaAction = action( + name: 'Run as Java', + closure: controller.&runJava, + mnemonic: 'J', + accelerator: shortcut('alt R'), + shortDescription: 'Execute Java Code' +) + loopModeAction = action( name: 'Loop Mode', closure: controller.&loopMode, @@ -219,6 +227,11 @@ runSelectionAction = action( accelerator: shortcut('shift R') ) +runJavaSelectionAction = action( + name: 'Run Java Selection', + closure: controller.&runSelectedJava +) + addClasspathJar = action( name: 'Add Jar(s) to ClassPath', closure: controller.&addClasspathJar, diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy index 4760c66..8252e33 100644 --- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy +++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy @@ -78,9 +78,11 @@ menuBar { menu(text: 'Script', mnemonic: 'S') { menuItem(runAction) + menuItem(runJavaAction) checkBoxMenuItem(loopModeAction, selected: controller.loopMode) checkBoxMenuItem(saveOnRunAction, selected: controller.saveOnRun) menuItem(runSelectionAction) + menuItem(runJavaSelectionAction) checkBoxMenuItem(threadInterruptAction, selected: controller.threadInterrupt) menuItem(interruptAction) menuItem(compileAction) diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy index 58a78a7..20a2cd9 100644 --- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy +++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy @@ -130,9 +130,11 @@ menuBar { menu(text: 'Script', mnemonic: 'S') { menuItem(runAction, icon:null) + menuItem(runJavaAction, icon:null) checkBoxMenuItem(loopModeAction, selected: controller.loopMode) checkBoxMenuItem(saveOnRunAction, selected: controller.saveOnRun) menuItem(runSelectionAction, icon:null) + menuItem(runJavaSelectionAction, icon:null) checkBoxMenuItem(threadInterruptAction, selected: controller.threadInterrupt) menuItem(interruptAction, icon:null) menuItem(compileAction, icon:null)
