Skip to content
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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

yanex
Copy link
Member

@yanex yanex commented Feb 7, 2025

No description provided.

@yanex yanex requested a review from marcopennekamp February 7, 2025 11:50
Copy link
Contributor

@marcopennekamp marcopennekamp left a 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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`.

Comment on lines +71 to +86
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`.
Copy link
Contributor

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Comment on lines +139 to +144
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)
```
Copy link
Contributor

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.

Comment on lines +162 to +167
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.
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants