-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add documentation for in-memory file analysis #7
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome writeup! It's very well written, has clear explanations, and covers all the important topics and nuances between different kinds of dangling files. Nice!
<note>Experimental API. Subject to changes.</note> | ||
|
||
Parsing a Kotlin file does not require any knowledge about the project it belongs to. On the other hand, for *semantic | ||
code analysis*, understanding dependencies and compilation options of file file is crucial. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
code analysis*, understanding dependencies and compilation options of file file is crucial. | |
code analysis*, understanding dependencies and compilation options of a file is crucial. |
scripts technically do not belong to any module. However, in such cases, the build system also provides the necessary | ||
context. For example, Gradle build scripts include the Gradle API and all libraries Gradle depends on in their classpath. | ||
|
||
In certain cases, it might be useful to analyze a file without storing it on the file system. For example, an IDE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In certain cases, it might be useful to analyze a file without storing it on the file system. For example, an IDE | |
In certain cases, it might be useful to analyze a file without storing it in the file system. For example, an IDE |
``` | ||
|
||
The `contextModule` returned is of type `KaModule` which is an Analysis API abstraction over `Module`, `Library` and | ||
`Sdk`concepts from IntelliJ IDEA. Specifically, a `KaSourceModule` represents a source module, and libraries and SDKs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`Sdk`concepts from IntelliJ IDEA. Specifically, a `KaSourceModule` represents a source module, and libraries and SDKs | |
`Sdk` concepts from IntelliJ IDEA. Specifically, a `KaSourceModule` represents a source module, and libraries and SDKs |
val contextModule = KaModuleProvider.getModule(project, contextFile, useSiteModule = null) | ||
``` | ||
|
||
The `contextModule` returned is of type `KaModule` which is an Analysis API abstraction over `Module`, `Library` and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The `contextModule` returned is of type `KaModule` which is an Analysis API abstraction over `Module`, `Library` and | |
The `contextModule` returned is of type `KaModule` which is an Analysis API abstraction over `Module`, `Library`, and |
|
||
The `contextModule` returned is of type `KaModule` which is an Analysis API abstraction over `Module`, `Library` and | ||
`Sdk`concepts from IntelliJ IDEA. Specifically, a `KaSourceModule` represents a source module, and libraries and SDKs | ||
are represented by `KaLibraryModule`s. Every `KaSymbol` in the Analysis API is associated to some `KaModule`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are represented by `KaLibraryModule`s. Every `KaSymbol` in the Analysis API is associated to some `KaModule`. | |
are represented by `KaLibraryModule`s. Every `KaSymbol` in the Analysis API is associated with some `KaModule`. |
If you already have a reference to a `Module`, you can convert it to a `KaModule` using one of the Kotlin plugin helper | ||
functions: | ||
|
||
```kotlin | ||
fun Module.toKaSourceModule(kind: KaSourceModuleKind): KaSourceModule? | ||
fun Module.toKaSourceModuleForProduction(): KaSourceModule? | ||
fun Module.toKaSourceModuleForTest(): KaSourceModule? | ||
``` | ||
|
||
For more related APIs, refer to the Kotlin plugin's | ||
[source code](https://github.com/JetBrains/intellij-community/blob/master/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/base/projectStructure/api.kt). | ||
There are also overloads that accept `ModuleId` and `ModuleEntity` from the newer project model API. | ||
|
||
In the `KaModuleProvider.getModule` function call, we passed `useSiteModule = null`. For advanced scenarios, | ||
you might want to analyze files from other modules in the context of a synthetic module. In such cases, that synthetic | ||
module can be passed as a `useSiteModule`. For typical use cases, it is safe to pass `null`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like there's a lot of detailed information here which breaks up the flow of the tutorial too much. Maybe some of these notes can be moved to a separate section?
If the context module includes a dependency to the Kotlin Standard library, the analysis will no longer produce errors. | ||
|
||
The created file can reference declarations from the context module, including `internal` ones. | ||
However, no matter what is be written in our newly created file, it will not affect resolution of our context file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, no matter what is be written in our newly created file, it will not affect resolution of our context file. | |
However, no matter the content of our newly created file, it will not affect resolution of our context file. |
If you need a long-lived file that will be modified and analyzed multiple times, you should create a physical file: | ||
|
||
```kotlin | ||
val factory = KtPsiFactory(project, eventSystemEnabled = true) | ||
val file = factory.createFile(text) | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be nice here to note that the eventSystemEnabled
setting is what makes the file physical. It's otherwise a bit hard to see and unintuitive.
The `copy()` function creates non-physical files. For this setup (a non-physical file with a `getOriginalFile()` set), | ||
the Analysis API uses a different analysis strategy by default. | ||
|
||
This behavior is optimized for efficiency. Since the original file might be large, analyzing it from scratch could be | ||
unnecessarily resource-intensive. In most cases, file copies primarily differ in declaration bodies, so the Analysis API | ||
leverages existing analysis results from the original file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the analyzeCopy
example below, I think it'd be nice to have an analyzeCopy
example here without the PREFER_SELF
mode, and then that note about the default mode.
No description provided.