This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 4fb7466 GROOVY-9468: Support running as Java code in the Groovy
console (#1192)
4fb7466 is described below
commit 4fb7466965a0b4b6bd12a7c5c93189168fe290b5
Author: Daniel.Sun <[email protected]>
AuthorDate: Mon Mar 16 02:56:39 2020 +0800
GROOVY-9468: Support running as Java code in the Groovy console (#1192)
* GROOVY-9468: Support running as Java code in the Groovy console
* Minor refactoring
---
subprojects/groovy-console/build.gradle | 1 +
.../main/groovy/groovy/console/ui/Console.groovy | 165 ++++++++++++++++++---
.../groovy/groovy/console/ui/ConsoleActions.groovy | 13 ++
.../groovy/console/ui/view/BasicMenuBar.groovy | 2 +
.../groovy/console/ui/view/MacOSXMenuBar.groovy | 2 +
5 files changed, 164 insertions(+), 19 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..37ec862 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.ParseProblemException
+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
@@ -913,15 +919,8 @@ class Console implements CaretListener, HyperlinkListener,
ComponentListener, Fo
int errorLine = se.line
String message = se.originalMessage
- String scriptFileName = scriptFile?.name ?:
DEFAULT_SCRIPT_NAME_START
-
def doc = outputArea.styledDocument
-
- def style = hyperlinkStyle
- def hrefAttr = new SimpleAttributeSet()
- // don't pass a GString as it won't be coerced to String
as addAttribute takes an Object
- hrefAttr.addAttribute(HTML.Attribute.HREF, 'file://' +
scriptFileName + ':' + errorLine)
- style.addAttribute(HTML.Tag.A, hrefAttr)
+ Style style = createLinkStyle(errorLine)
insertString(doc, doc.length, message + ' at ',
stacktraceStyle)
insertString(doc, doc.length, "line: ${se.line}, column:
${se.startColumn}\n\n", style)
@@ -934,6 +933,29 @@ class Console implements CaretListener, HyperlinkListener,
ComponentListener, Fo
insertString(doc, doc.length, "${error.message}\n", new
SimpleAttributeSet())
}
}
+ } else if (t instanceof ParseProblemException) {
+ def problemList = ((ParseProblemException) t).getProblems()
+ int count = problemList.size()
+ appendOutputNl("${count} compilation error${count > 1 ? 's' :
''}:\n\n", commandStyle)
+
+ def doc = outputArea.styledDocument
+ problemList.each { p ->
+ insertString(doc, doc.length, "${p.message}", stacktraceStyle)
+
+ if (p.location.isPresent()) {
+ def range = p.location.get().begin.range
+ if (range.isPresent()) {
+ def position = range.get().begin
+ def errorLine = position.line
+ def errorCol = position.column
+ Style style = createLinkStyle(errorLine)
+
+ insertString(doc, doc.length, " at ", stacktraceStyle)
+ insertString(doc, doc.length, "line: ${errorLine},
column: ${errorCol}\n\n", style)
+ }
+ }
+
+ }
} else {
reportException(t)
}
@@ -951,6 +973,16 @@ class Console implements CaretListener, HyperlinkListener,
ComponentListener, Fo
}
}
+ private Style createLinkStyle(int errorLine) {
+ String scriptFileName = scriptFile?.name ?: DEFAULT_SCRIPT_NAME_START
+ def style = hyperlinkStyle
+ def hrefAttr = new SimpleAttributeSet()
+ // don't pass a GString as it won't be coerced to String as
addAttribute takes an Object
+ hrefAttr.addAttribute(HTML.Attribute.HREF, 'file://' + scriptFileName
+ ':' + errorLine)
+ style.addAttribute(HTML.Tag.A, hrefAttr)
+ return style
+ }
+
private calcPreferredSize(a, b, c) {
[c, [a, b].min()].max()
}
@@ -1136,13 +1168,74 @@ class Console implements CaretListener,
HyperlinkListener, ComponentListener, Fo
return consoleControllers.find { it.consoleId == consoleId }
}
+ @CompileStatic
+ private class GroovySourceType extends SourceType {
+ GroovySourceType() {
+ super('groovy')
+ }
+
+ @Override
+ Object run(String src) {
+ String name = ((File) Console.this.scriptFile)?.name ?:
(DEFAULT_SCRIPT_NAME_START + Console.this.scriptNameCounter++)
+ Console.this.shell.run(src, name, [])
+ }
+ }
+
+ @CompileStatic
+ private class JavaSourceType extends SourceType {
+ JavaSourceType() {
+ super('java')
+ }
+
+ @Override
+ Object run(String src) {
+ Optional<String> optionalPrimaryClassName =
findPrimaryClassName(src)
+ if (optionalPrimaryClassName.isPresent()) {
+ def js = new
JavaShell(Thread.currentThread().contextClassLoader)
+ js.run(optionalPrimaryClassName.get(), src)
+ }
+ return null
+ }
+ }
+
+ @CompileStatic
+ private abstract class SourceType {
+ String extension
+ SourceType(String extension) {
+ this.extension = extension
+ }
+
+ abstract Object run(String src)
+
+ @Override
+ boolean equals(o) {
+ if (this.is(o)) return true
+ if (!(o instanceof SourceType)) return false
+
+ SourceType that = (SourceType) o
+
+ if (extension != that.extension) return false
+
+ return true
+ }
+
+ @Override
+ int hashCode() {
+ return extension.hashCode()
+ }
+ }
+
+ void runJava(EventObject evt = null) {
+ runScript(evt, new JavaSourceType())
+ }
+
// actually run the script
- void runScript(EventObject evt = null) {
+ void runScript(EventObject evt = null, SourceType st = new
GroovySourceType()) {
saveInputAreaContentHash()
if (saveOnRun && scriptFile != null) {
- if (fileSave(evt)) runScriptImpl(false)
+ if (fileSave(evt)) runScriptImpl(false, st)
} else {
- runScriptImpl(false)
+ runScriptImpl(false, st)
}
}
@@ -1180,9 +1273,13 @@ class Console implements CaretListener,
HyperlinkListener, ComponentListener, Fo
cc.getOptimizationOptions().get(CompilerConfiguration.INVOKEDYNAMIC)
}
- void runSelectedScript(EventObject evt = null) {
+ void runSelectedJava(EventObject evt = null) {
+ runSelectedScript(evt, new JavaSourceType())
+ }
+
+ void runSelectedScript(EventObject evt = null, SourceType st = new
GroovySourceType()) {
saveInputAreaContentHash()
- runScriptImpl(true)
+ runScriptImpl(true, st)
}
void addClasspathJar(EventObject evt = null) {
@@ -1251,7 +1348,7 @@ class Console implements CaretListener,
HyperlinkListener, ComponentListener, Fo
inputAreaContentHash = inputArea.getText().hashCode()
}
- private void runScriptImpl(boolean selected) {
+ private void runScriptImpl(boolean selected, SourceType st = new
GroovySourceType()) {
if (scriptRunning) {
statusLabel.text = 'Cannot run script now as a script is already
running. Please wait or use "Interrupt Script" option.'
return
@@ -1269,8 +1366,9 @@ class Console implements CaretListener,
HyperlinkListener, ComponentListener, Fo
// Print the input text
if (showScriptInOutput) {
+ final promptPrefix = "${st.extension}> "
for (line in record.getTextToRun(selected).tokenize('\n')) {
- appendOutputNl('groovy> ', promptStyle)
+ appendOutputNl(promptPrefix, promptStyle)
appendOutput(line, commandStyle)
}
appendOutputNl(' \n', promptStyle)
@@ -1282,7 +1380,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 +1388,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 +1416,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 +1426,36 @@ class Console implements CaretListener,
HyperlinkListener, ComponentListener, Fo
}
}
+ @CompileStatic
+ private Object doRun(boolean selected, SourceType st, HistoryRecord
record) {
+ def src = record.getTextToRun(selected)
+ return st.run(src)
+ }
+
+ @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..6e06db3 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 Selection as Java',
+ 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)