5

I want to use Kotlin worksheets in my Android project in order to add code drafts like:

draft.ws.kts

package com.example.app

val a = 1 + 1
a

The worksheet itself is working:

val a: Int
2

But building my Android app fails with the following output:

> Task :appicals:compileDebugKotlin FAILED
e: org.jetbrains.kotlin.util.KotlinFrontEndException: Front-end Internal error: Failed to analyze declaration Draft_ws
File being compiled: (1,43) in /Users/me/secretproject/app/src/main/java/com/example/app/draft.ws.kts
The root cause org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException was thrown at: org.jetbrains.kotlin.resolve.lazy.BasicAbsentDescriptorHandler.diagnoseDescriptorNotFound(AbsentDescriptorHandler.kt:18)
    at org.jetbrains.kotlin.resolve.ExceptionWrappingKtVisitorVoid.visitDeclaration(ExceptionWrappingKtVisitorVoid.kt:43)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitDeclaration(KtVisitorVoid.java:453)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitDeclaration(KtVisitorVoid.java:21)
    at org.jetbrains.kotlin.psi.KtVisitor.visitScript(KtVisitor.java:78)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitScript(KtVisitorVoid.java:73)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitScript(KtVisitorVoid.java:519)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitScript(KtVisitorVoid.java:21)
    at org.jetbrains.kotlin.psi.KtScript.accept(KtScript.java:69)
    at org.jetbrains.kotlin.psi.KtElementImplStub.accept(KtElementImplStub.java:59)
    at org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer$analyzeDeclarations$1.registerDeclarations(LazyTopDownAnalyzer.kt:78)
    at org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer$analyzeDeclarations$1.visitKtFile(LazyTopDownAnalyzer.kt:96)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:513)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:21)
    at org.jetbrains.kotlin.psi.KtFile.accept(KtFile.kt:242)
    at org.jetbrains.kotlin.psi.KtFile.accept(KtFile.kt:229)
    at org.jetbrains.kotlin.resolve.ExceptionWrappingKtVisitorVoid.visitElement(ExceptionWrappingKtVisitorVoid.kt:27)
    at org.jetbrains.kotlin.com.intellij.psi.PsiElementVisitor.visitFile(PsiElementVisitor.java:34)
    at org.jetbrains.kotlin.psi.KtVisitor.visitKtFile(KtVisitor.java:73)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:69)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:513)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:21)
    at org.jetbrains.kotlin.psi.KtFile.accept(KtFile.kt:242)
    at org.jetbrains.kotlin.psi.KtFile.accept(KtFile.kt:229)
    at org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer.analyzeDeclarations(LazyTopDownAnalyzer.kt:201)
    at org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer.analyzeDeclarations$default(LazyTopDownAnalyzer.kt:60)
    at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:112)
    at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:82)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:554)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:81)
    at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:107)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:545)
    at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:176)
    at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:163)
    at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:51)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:85)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:43)
    at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:104)
    at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:349)
    at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:105)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:237)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.access$compileIncrementally(IncrementalCompilerRunner.kt:37)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner$compile$2.invoke(IncrementalCompilerRunner.kt:79)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:91)
    at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:606)
    at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:99)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1645)
    at sun.reflect.GeneratedMethodAccessor182.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
    at sun.rmi.transport.Transport$1.run(Transport.java:200)
    at sun.rmi.transport.Transport$1.run(Transport.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException: Descriptor wasn't found for declaration SCRIPT
    at org.jetbrains.kotlin.resolve.lazy.BasicAbsentDescriptorHandler.diagnoseDescriptorNotFound(AbsentDescriptorHandler.kt:18)
    at org.jetbrains.kotlin.resolve.lazy.BasicAbsentDescriptorHandler.diagnoseDescriptorNotFound(AbsentDescriptorHandler.kt:17)
    at org.jetbrains.kotlin.resolve.lazy.LazyDeclarationResolver.findClassDescriptor(LazyDeclarationResolver.kt:88)
    at org.jetbrains.kotlin.resolve.lazy.LazyDeclarationResolver.getScriptDescriptor(LazyDeclarationResolver.kt:65)
    at org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer$analyzeDeclarations$1.visitScript(LazyTopDownAnalyzer.kt:89)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitScript(KtVisitorVoid.java:519)
    at org.jetbrains.kotlin.psi.KtVisitorVoid.visitScript(KtVisitorVoid.java:21)
    at org.jetbrains.kotlin.psi.KtScript.accept(KtScript.java:69)
    at org.jetbrains.kotlin.psi.KtElementImplStub.accept(KtElementImplStub.java:59)
    at org.jetbrains.kotlin.resolve.ExceptionWrappingKtVisitorVoid.visitDeclaration(ExceptionWrappingKtVisitorVoid.kt:32)
    ... 61 more

The dependency is implementation "org.jetbrains.kotlin:kotlin-script-runtime:1.3.70"

mbo
  • 4,611
  • 2
  • 34
  • 54

2 Answers2

1

Edit (2021-04-11): This is an ugly kludge which has no right to exist. Please read my shinier, newer, better answer.

Not sure if this counts as a great answer, since it doesn't just completely fix it, but the gist of it is... change the file extension. Just add an underscore at the end or something.

OMFSM I spent so long trying to find a proper way to do it. And then trying a million different kludges. And I didn't even have this problem before (didn't even know worksheets existed TBH), it just looked like a good idea.

There's a couple of ways to make that easier, but it still requires human intervention whenever you want to build (or you can just leave them off until you need them again).

Personally I just created a couple of batch files (although they're each only one command), which rename all the .kts files (and then rename them back) and created some handy shortcuts. I'm running Windows, and bash scripting is definitely not in my skill set, so I'm sure this can be done on *nix too.

To rename them all so they don't try and be compiled:

forfiles /S /M *.ws.kts /C "cmd /c ren @file @fname.kts_"

and to turn them back into valid worksheets:

forfiles /S /M *.ws.kts_ /C "cmd /c ren @file @fname.kts"

To make a batch file out of one of those:

  1. Open Notepad
  2. Copy one of those lines into it.
  3. Go to File > Save, navigate to the folder your project is saved in and give it a filename surrounded by double quotes (") and ending in .bat
    • For example "disable_worksheets.bat" and "enable_worksheets.bat" but make sure to include the double quotes.

Now you can just find that file in Windows Explorer and double click it to disable or enable them all at once.

If you want, you can also make a macro to do it each way in Android Studio, but AFAIK that can only change one file at a time (though you can set a keyboard shortcut, so it works if you don't have too many worksheets). To make the macro to disable a worksheet:

  1. Select your worksheet file in tree view on left
  2. In the menu bar, go to Edit > Macros and click "Start Macro Recording"
  3. Right click your worksheet in the tree view, and under the Refactor menu click "Rename file..."
  4. Press End on your keyboard
  5. Type an underscore _
  6. Press Enter
  7. In the menu bar, go to Edit > Macros and click "Stop Macro Recording"
  8. Give your macro a name and click OK

And for the one to re-enable it:

  1. Select your worksheet file in tree view on left
  2. In the menu bar, go to Edit > Macros and click "Start Macro Recording"
  3. Right click your worksheet in the tree view, and under the Refactor menu click "Rename..."
  4. Press End on your keyboard
  5. Press Backspace
  6. Press Enter
  7. In the menu bar, go to Edit > Macros and click "Stop Macro Recording"
  8. Give your macro a name and click OK

Now you can just select a file in the tree view, then in the menus go to Edit > Macros and click the one you need. But to make it more convenient, you can set keyboard shortcuts. To do that:

  1. In the menu bar, go to File and click "Settings..."
  2. In the menu on the left, click "Keymap"
  3. In the main pane of the editor, click the next to "Macros" to expand that section
  4. Right click the macro you want to set a shortcut for, and click "Add Keyboard Shortcut"
  5. Press the keys for the shortcut you want to use (for example Ctrl + Comma). It will warn you if you if a shortcut already uses those keys, so you may have to use a couple of modifiers (for example Ctrl + Shift + G)
    • A few I've found that are empty by default (at least on my install) and use only Ctrl as a modifier are Ctrl + Comma (,), Ctrl + Semicolon (;) and Ctrl + Backslash (\).
    • Most of the numpad keys are unused, but obviously they're not as convenient.
  6. Click OK to get back to the main settings window.
  7. If you want to set a shortcut for the other macro, just redo steps 4-6
  8. Click OK at the bottom of the settings window.

With those set all you have to do is click a filename in the tree view on the left and press your keyboard shortcut.

Hope all that helps someone! And sorry if this was a bit long, being terse isn't something in my skill set either.

Slashee the Cow
  • 315
  • 3
  • 8
0

TL;DR: IntelliJ supports it great. Android Studio hides it.

1: Create a new file by right clicking in the project tree and selecting New ► Scratch File or press Ctrl+Alt+Shift+Insert and choose Kotlin from the list that comes up.

It stores these in the IDE's config folder so Gradle doesn't get anywhere near them, but there's an easy setting in the editor for each file to use the classpath for the open project. This also means they're shared between projects.

2: In the project view on the left, at the top, click Android and select Project. They'll be in a folder at the bottom called Scratches and Consoles ► Scratches and open your worksheet(s). You can switch back to Android view now.

3: Blame Google.


Here's the long story:

Okay so playing around and having done more research - which I probably should have done in the first place - IntelliJ supports this feature (quite well, by the looks of it) but Android Studio sort of hides it.

IntelliJ calls them scratch files (sounds about right). They're stored in the IDE's settings folder (in my case %APPDATA%\Google\AndroidStudio4.1\scratches) so Gradle doesn't even see them. Being stored in the IDE's settings, they're also global, not part of a specific project, so you can access them from any project.

To create one, right click anywhere in the project tool pane on the left and choose New ► Scratch File or press Ctrl+Alt+Shift+Insert. That'll bring up a menu of languages, seems to default to Kotlin for me (possibly because the project I have open is in Kotlin), with that selected, just press enter. That'll bring up the file as a tab in the editor, code on the left, result on the right. At the top there's an option labelled Use classpath of module with a dropdown to the right, you can pick a module in the currently open project (you know, for testing stuff with the project).

By default the file is named scratch.kts (or if you have more than one, scratch_1.kts, scratch_2.kts, etc. You can rename it if you want by right clicking its tab in the editor windows and selecting Rename file...

Here's the important bit:

In Android Studio, the default scope for the project tree (by default the toolpane at the left of the window) is set to Android. If you click that and select Project. At the bottom there'll be a folder called Scratches and Consoles with a subfolder in there called Scratches. Et voilà! Your scratch files are there. Obviously in the generic project view you lose all the Android niceties so you can switch back now, just remember how to get back there. If there's a way to get them to show up, I can't find it (but in case you couldn't tell by now, I'm not an expert Android programmer, just a smartarse with too much free time).

Slashee the Cow
  • 315
  • 3
  • 8