├── iosApp
├── Configuration
│ └── Config.xcconfig
├── iosApp
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── app-icon-1024.png
│ │ │ └── Contents.json
│ │ └── AccentColor.colorset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── iOSApp.swift
│ ├── ContentView.swift
│ └── Info.plist
└── iosApp.xcodeproj
│ └── project.pbxproj
├── composeApp
├── src
│ ├── androidMain
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── kotlin
│ │ │ ├── Platform.android.kt
│ │ │ └── org
│ │ │ │ └── example
│ │ │ │ └── project
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── commonMain
│ │ ├── kotlin
│ │ │ ├── Platform.kt
│ │ │ ├── Greeting.kt
│ │ │ ├── LLMProviderType.kt
│ │ │ ├── ModelRegistry.kt
│ │ │ └── App.kt
│ │ └── composeResources
│ │ │ └── drawable
│ │ │ └── compose-multiplatform.xml
│ ├── iosMain
│ │ └── kotlin
│ │ │ ├── MainViewController.kt
│ │ │ └── Platform.ios.kt
│ ├── desktopMain
│ │ └── kotlin
│ │ │ ├── Platform.jvm.kt
│ │ │ └── main.kt
│ └── wasmJsMain
│ │ ├── kotlin
│ │ ├── Platform.wasmJs.kt
│ │ ├── CodeLanguage.kt
│ │ ├── GitInterop.kt
│ │ ├── main.kt
│ │ ├── GitRepository.kt
│ │ ├── JavaParser.kt
│ │ ├── ParserDemo.kt
│ │ └── TreeSitterInterop.kt
│ │ └── resources
│ │ └── index.html
├── webpack.config.d
│ └── node-polyfills.js
└── build.gradle.kts
├── screenshots
└── compose-example.png
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── .gitignore
├── settings.gradle.kts
├── gradle.properties
├── .fleet
└── receipt.json
├── .github
└── workflows
│ └── ci.yml
├── gradlew.bat
├── README.md
├── web
└── libgit2_webworker.js
├── gradlew
└── LICENSE
/iosApp/Configuration/Config.xcconfig:
--------------------------------------------------------------------------------
1 | TEAM_ID=
2 | BUNDLE_ID=org.example.project.composeexample
3 | APP_NAME=composeexample
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | composeexample
3 |
--------------------------------------------------------------------------------
/screenshots/compose-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/screenshots/compose-example.png
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/Platform.kt:
--------------------------------------------------------------------------------
1 | interface Platform {
2 | val name: String
3 | }
4 |
5 | expect fun getPlatform(): Platform
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/MainViewController.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.window.ComposeUIViewController
2 |
3 | fun MainViewController() = ComposeUIViewController { App() }
4 |
--------------------------------------------------------------------------------
/iosApp/iosApp/iOSApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct iOSApp: App {
5 | var body: some Scene {
6 | WindowGroup {
7 | ContentView()
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/Greeting.kt:
--------------------------------------------------------------------------------
1 | class Greeting {
2 | private val platform = getPlatform()
3 |
4 | fun greet(): String {
5 | return "Hello, ${platform.name}!"
6 | }
7 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/compose-treesitter-wasmjs-demo/main/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
--------------------------------------------------------------------------------
/composeApp/src/desktopMain/kotlin/Platform.jvm.kt:
--------------------------------------------------------------------------------
1 | class JVMPlatform: Platform {
2 | override val name: String = "Java ${System.getProperty("java.version")}"
3 | }
4 |
5 | actual fun getPlatform(): Platform = JVMPlatform()
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/Platform.wasmJs.kt:
--------------------------------------------------------------------------------
1 | private val platform = object : Platform {
2 | override val name: String
3 | get() = "Web with Kotlin/Wasm"
4 | }
5 |
6 | actual fun getPlatform(): Platform = platform
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/Platform.android.kt:
--------------------------------------------------------------------------------
1 | import android.os.Build
2 |
3 | class AndroidPlatform : Platform {
4 | override val name: String = "Android ${Build.VERSION.SDK_INT}"
5 | }
6 |
7 | actual fun getPlatform(): Platform = AndroidPlatform()
--------------------------------------------------------------------------------
/composeApp/src/iosMain/kotlin/Platform.ios.kt:
--------------------------------------------------------------------------------
1 | import platform.UIKit.UIDevice
2 |
3 | class IOSPlatform: Platform {
4 | override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
5 | }
6 |
7 | actual fun getPlatform(): Platform = IOSPlatform()
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "app-icon-1024.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .kotlin
4 | **/build/
5 | xcuserdata
6 | !src/**/build/
7 | local.properties
8 | .idea
9 | .DS_Store
10 | captures
11 | .externalNativeBuild
12 | .cxx
13 | *.xcodeproj/*
14 | !*.xcodeproj/project.pbxproj
15 | !*.xcodeproj/xcshareddata/
16 | !*.xcodeproj/project.xcworkspace/
17 | !*.xcworkspace/contents.xcworkspacedata
18 | **/xcshareddata/WorkspaceSettings.xcsettings
19 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/LLMProviderType.kt:
--------------------------------------------------------------------------------
1 | enum class LLMProviderType(val displayName: String) {
2 | OPENAI("OpenAI"),
3 | ANTHROPIC("Anthropic"),
4 | GOOGLE("Google"),
5 | DEEPSEEK("DeepSeek"),
6 | OLLAMA("Ollama"),
7 | OPENROUTER("OpenRouter"),
8 | GLM("GLM"),
9 | QWEN("Qwen"),
10 | KIMI("Kimi"),
11 | CUSTOM_OPENAI_BASE("custom-openai-base");
12 | }
13 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "compose-example"
2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
3 |
4 | pluginManagement {
5 | repositories {
6 | google()
7 | gradlePluginPortal()
8 | mavenCentral()
9 | }
10 | }
11 |
12 | dependencyResolutionManagement {
13 | repositories {
14 | google()
15 | mavenCentral()
16 | }
17 | }
18 |
19 | include(":composeApp")
--------------------------------------------------------------------------------
/composeApp/src/desktopMain/kotlin/main.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.desktop.ui.tooling.preview.Preview
2 | import androidx.compose.runtime.Composable
3 | import androidx.compose.ui.window.Window
4 | import androidx.compose.ui.window.application
5 |
6 | fun main() = application {
7 | Window(onCloseRequest = ::exitApplication) {
8 | App()
9 | }
10 | }
11 |
12 | @Preview
13 | @Composable
14 | fun AppDesktopPreview() {
15 | App()
16 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
2 |
3 | #Gradle
4 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
5 |
6 |
7 | #Compose for Web is Experimental
8 | org.jetbrains.compose.experimental.wasm.enabled=true
9 |
10 | #Android
11 | android.useAndroidX=true
12 | android.nonTransitiveRClass=true
13 |
14 | #MPP
15 | kotlin.mpp.androidSourceSetLayoutVersion=2
16 | kotlin.mpp.enableCInteropCommonization=true
17 |
18 | #Development
19 | development=true
--------------------------------------------------------------------------------
/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SwiftUI
3 | import ComposeApp
4 |
5 | struct ComposeView: UIViewControllerRepresentable {
6 | func makeUIViewController(context: Context) -> UIViewController {
7 | MainViewControllerKt.MainViewController()
8 | }
9 |
10 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
11 | }
12 |
13 | struct ContentView: View {
14 | var body: some View {
15 | ComposeView()
16 | .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
17 | }
18 | }
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.fleet/receipt.json:
--------------------------------------------------------------------------------
1 | // Project generated by Kotlin Multiplatform Wizard
2 | {
3 | "spec": {
4 | "template_id": "kmt",
5 | "targets": {
6 | "android": {
7 | "ui": [
8 | "compose"
9 | ]
10 | },
11 | "ios": {
12 | "ui": [
13 | "compose"
14 | ]
15 | },
16 | "desktop": {
17 | "ui": [
18 | "compose"
19 | ]
20 | }
21 | }
22 | },
23 | "timestamp": "2023-11-28T16:28:52.915133231Z"
24 | }
--------------------------------------------------------------------------------
/composeApp/src/androidMain/kotlin/org/example/project/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package org.example.project
2 |
3 | import App
4 | import android.os.Bundle
5 | import androidx.activity.ComponentActivity
6 | import androidx.activity.compose.setContent
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.tooling.preview.Preview
9 |
10 | class MainActivity : ComponentActivity() {
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 |
14 | setContent {
15 | App()
16 | }
17 | }
18 | }
19 |
20 | @Preview
21 | @Composable
22 | fun AppAndroidPreview() {
23 | App()
24 | }
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/CodeLanguage.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Supported programming languages for tree-sitter parsing
3 | */
4 | enum class CodeLanguage {
5 | JAVA,
6 | KOTLIN,
7 | JAVASCRIPT,
8 | TYPESCRIPT,
9 | PYTHON,
10 | RUST,
11 | GO,
12 | C,
13 | CPP,
14 | CSHARP,
15 | RUBY,
16 | PHP,
17 | SWIFT,
18 | SCALA,
19 | LUA,
20 | BASH;
21 |
22 | /**
23 | * Get the language identifier used in tree-sitter WASM file names
24 | */
25 | fun getLanguageId(): String {
26 | return when (this) {
27 | CSHARP -> "c_sharp"
28 | CPP -> "cpp"
29 | else -> name.lowercase()
30 | }
31 | }
32 |
33 | /**
34 | * Get the path to the language WASM file
35 | */
36 | fun getWasmPath(): String {
37 | return "wasm/tree-sitter-${getLanguageId()}.wasm"
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/ModelRegistry.kt:
--------------------------------------------------------------------------------
1 | import ai.koog.prompt.llm.LLMCapability
2 | import ai.koog.prompt.llm.LLMProvider
3 | import ai.koog.prompt.llm.LLModel
4 |
5 | class ModelRegistry {
6 | fun createGenericModel(
7 | provider: LLMProviderType,
8 | modelName: String,
9 | contextLength: Long = 128000L
10 | ): LLModel {
11 | val llmProvider = if (provider == LLMProviderType.OPENAI) LLMProvider.OpenAI
12 | else if (provider == LLMProviderType.ANTHROPIC) LLMProvider.Anthropic
13 | else if (provider == LLMProviderType.GOOGLE) LLMProvider.Google
14 | else if (provider == LLMProviderType.DEEPSEEK) LLMProvider.DeepSeek
15 | else if (provider == LLMProviderType.OLLAMA) LLMProvider.Ollama
16 | else if (provider == LLMProviderType.OPENROUTER) LLMProvider.OpenRouter
17 | else if (provider == LLMProviderType.GLM) LLMProvider.OpenAI // Use OpenAI-compatible provider
18 | else if (provider == LLMProviderType.QWEN) LLMProvider.OpenAI // Use OpenAI-compatible provider
19 | else if (provider == LLMProviderType.KIMI) LLMProvider.OpenAI // Use OpenAI-compatible provider
20 | else if (provider == LLMProviderType.CUSTOM_OPENAI_BASE) LLMProvider.OpenAI // Use OpenAI-compatible provider
21 | else LLMProvider.OpenAI
22 |
23 | val capabilities = listOf(LLMCapability.Completion, LLMCapability.Temperature)
24 |
25 | return LLModel(
26 | LLMProvider.OpenAI,
27 | modelName,
28 | capabilities,
29 | contextLength
30 | )
31 | }
32 | }
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Publish Application on GitHub Pages
2 | on:
3 | push:
4 | branches:
5 | - main
6 |
7 | jobs:
8 | # Build job
9 | build:
10 | name: Build Kotlin/Wasm
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Set up JDK
14 | uses: actions/setup-java@v2
15 | with:
16 | distribution: 'adopt'
17 | java-version: '17'
18 |
19 | - name: Checkout code
20 | uses: actions/checkout@v3
21 |
22 | - name: Run Gradle Tasks
23 | run: ./gradlew wasmJsBrowserDistribution
24 |
25 | - name: Fix permissions
26 | run: |
27 | chmod -v -R +rX "composeApp/build/dist/wasmJs/productionExecutable/" | while read line; do
28 | echo "::warning title=Invalid file permissions automatically fixed::$line"
29 | done
30 |
31 | - name: Upload Pages artifact
32 | uses: actions/upload-pages-artifact@v3
33 | with:
34 | path: composeApp/build/dist/wasmJs/productionExecutable/
35 |
36 | deploy:
37 | # Add a dependency to the build job
38 | needs: build
39 |
40 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment
41 | permissions:
42 | pages: write # to deploy to Pages
43 | id-token: write # to verify the deployment originates from an appropriate source
44 |
45 | # Deploy to the github-pages environment
46 | environment:
47 | name: github-pages
48 | url: ${{ steps.deployment.outputs.page_url }}
49 |
50 | # Specify runner + deployment step
51 | runs-on: ubuntu-latest
52 | steps:
53 | - name: Deploy to GitHub Pages
54 | id: deployment
55 | uses: actions/deploy-pages@v4
56 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | CADisableMinimumFrameDurationOnPhone
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/composeApp/src/commonMain/kotlin/App.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.animation.AnimatedVisibility
2 | import androidx.compose.foundation.Image
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.material.Button
6 | import androidx.compose.material.MaterialTheme
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.getValue
10 | import androidx.compose.runtime.mutableStateOf
11 | import androidx.compose.runtime.remember
12 | import androidx.compose.runtime.setValue
13 | import androidx.compose.ui.Alignment
14 | import androidx.compose.ui.Modifier
15 | import compose_example.composeapp.generated.resources.Res
16 | import compose_example.composeapp.generated.resources.compose_multiplatform
17 | import org.jetbrains.compose.resources.ExperimentalResourceApi
18 | import org.jetbrains.compose.resources.painterResource
19 |
20 | @OptIn(ExperimentalResourceApi::class)
21 | @Composable
22 | fun App() {
23 | MaterialTheme {
24 | var greetingText by remember { mutableStateOf("Hello World!") }
25 | var showImage by remember { mutableStateOf(false) }
26 | Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
27 | Button(onClick = {
28 | greetingText = "Compose: ${Greeting().greet()}"
29 | showImage = !showImage
30 | }) {
31 | Text(greetingText)
32 | val createGenericModel = ModelRegistry().createGenericModel(LLMProviderType.OPENAI, "gpt-3.5-turbo")
33 | }
34 | AnimatedVisibility(showImage) {
35 | Image(
36 | painterResource(Res.drawable.compose_multiplatform),
37 | null
38 | )
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | compose = "1.8.2"
3 | agp = "8.1.4"
4 | android-minSdk = "24"
5 | android-compileSdk = "34"
6 | android-targetSdk = "34"
7 | androidx-activityCompose = "1.8.1"
8 | androidx-core-ktx = "1.12.0"
9 | androidx-appcompat = "1.6.1"
10 | androidx-material = "1.10.0"
11 | androidx-constraintlayout = "2.1.4"
12 | androidx-test-junit = "1.1.5"
13 | androidx-espresso-core = "3.5.1"
14 | kotlin = "2.2.0"
15 | junit = "4.13.2"
16 |
17 | [libraries]
18 | kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
19 | kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
20 | junit = { group = "junit", name = "junit", version.ref = "junit" }
21 | compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
22 | compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
23 | compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
24 | compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }
25 | compose-material = { module = "androidx.compose.material:material", version.ref = "compose" }
26 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
27 | androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }
28 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" }
29 | androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
30 | androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" }
31 | androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" }
32 | androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
33 |
34 | [plugins]
35 | androidApplication = { id = "com.android.application", version.ref = "agp" }
36 | androidLibrary = { id = "com.android.library", version.ref = "agp" }
37 | kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
38 | pluginCompose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
39 | jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose" }
40 |
--------------------------------------------------------------------------------
/composeApp/webpack.config.d/node-polyfills.js:
--------------------------------------------------------------------------------
1 | // Webpack configuration to handle Node.js modules in browser environment for wasmJs target
2 | // This file provides fallbacks for Node.js core modules when running tests in browser
3 | // Specifically needed for web-tree-sitter which may try to use Node.js APIs
4 |
5 | const CopyWebpackPlugin = require('copy-webpack-plugin');
6 |
7 | config.resolve = config.resolve || {};
8 | config.resolve.fallback = config.resolve.fallback || {};
9 |
10 | // Provide empty modules for Node.js core modules that are not available in browser
11 | // These are required by web-tree-sitter and wasm-git when running in browser/wasm environment
12 | config.resolve.fallback.fs = false;
13 | config.resolve.fallback.path = false;
14 | config.resolve.fallback.os = false;
15 | config.resolve.fallback.child_process = false;
16 |
17 | // Also handle other common Node.js modules that might be used
18 | config.resolve.fallback.crypto = false;
19 | config.resolve.fallback.stream = false;
20 | config.resolve.fallback.util = false;
21 | config.resolve.fallback.buffer = false;
22 | config.resolve.fallback.process = false;
23 |
24 | // Configure module resolution for wasm-git
25 | config.resolve.alias = config.resolve.alias || {};
26 | config.resolve.alias['module'] = false;
27 |
28 | // Ignore dynamic requires in wasm-git that can't be resolved
29 | config.module = config.module || {};
30 | config.module.unknownContextCritical = false;
31 | config.module.exprContextCritical = false;
32 |
33 | // Add webpack plugin to ignore wasm-git's dynamic requires
34 | const webpack = require('webpack');
35 | config.plugins.push(
36 | new webpack.ContextReplacementPlugin(
37 | /wasm-git/,
38 | (data) => {
39 | // Ignore all dynamic requires in wasm-git
40 | delete data.dependencies[0].critical;
41 | return data;
42 | }
43 | )
44 | );
45 |
46 | // Ignore specific problematic requires in wasm-git
47 | config.plugins.push(
48 | new webpack.IgnorePlugin({
49 | resourceRegExp: /^\.\/$/,
50 | contextRegExp: /wasm-git/
51 | })
52 | );
53 |
54 | // Copy tree-sitter.wasm and language WASM files to the output directory
55 | config.plugins = config.plugins || [];
56 | config.plugins.push(
57 | new CopyWebpackPlugin({
58 | patterns: [
59 | {
60 | from: '../../node_modules/wasm-git/lg2_async.wasm',
61 | to: 'lg2_async.wasm'
62 | },
63 | {
64 | from: '../../node_modules/web-tree-sitter/tree-sitter.wasm',
65 | to: 'tree-sitter.wasm'
66 | },
67 | {
68 | from: '../../node_modules/@unit-mesh/treesitter-artifacts/wasm/*.wasm',
69 | to: 'wasm/[name][ext]'
70 | }
71 | ]
72 | })
73 | );
74 |
75 | console.log('Applied Node.js polyfills for mpp-codegraph wasmJs browser tests');
76 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/GitInterop.kt:
--------------------------------------------------------------------------------
1 | import kotlin.js.Promise
2 |
3 | /**
4 | * Module configuration for wasm-git
5 | */
6 | external interface ModuleConfig : JsAny {
7 | var print: ((String) -> Unit)?
8 | var printErr: ((String) -> Unit)?
9 | var onRuntimeInitialized: (() -> Unit)?
10 | }
11 |
12 | /**
13 | * Import wasm-git/lg2.js as default export
14 | * lg2.js exports an async function: async function(moduleArg = {})
15 | *
16 | * https://raw.githubusercontent.com/petersalomonsen/githttpserver/refs/heads/master/public/libgit2_webworker.js
17 | */
18 | @JsModule("wasm-git/lg2_async.js")
19 | external fun lg2(config: ModuleConfig? = definedExternally): Promise
20 |
21 | /**
22 | * LibGit2 Module - returned by lg2() function
23 | */
24 | external interface LibGit2Module : JsAny {
25 | val FS: EmscriptenFS
26 |
27 | /**
28 | * Called when WASM runtime is initialized
29 | */
30 | var onRuntimeInitialized: (() -> Unit)?
31 |
32 | /**
33 | * Call git command with arguments
34 | * Returns exit code (0 for success)
35 | */
36 | fun callMain(args: JsArray): Promise
37 | }
38 |
39 | /**
40 | * Emscripten File System API
41 | */
42 | external interface EmscriptenFS : JsAny {
43 | /**
44 | * Write a file to the virtual file system
45 | */
46 | fun writeFile(path: String, data: String)
47 |
48 | /**
49 | * Read a file from the virtual file system
50 | */
51 | fun readFile(path: String): JsArray
52 |
53 | /**
54 | * Read directory contents
55 | */
56 | fun readdir(path: String): JsArray
57 |
58 | /**
59 | * Create a directory
60 | */
61 | fun mkdir(path: String)
62 |
63 | /**
64 | * Change current directory
65 | */
66 | fun chdir(path: String)
67 |
68 | /**
69 | * Sync file system with IndexedDB
70 | * @param populate true to load from IndexedDB, false to save to IndexedDB
71 | * @param callback callback function
72 | */
73 | fun syncfs(populate: Boolean, callback: () -> Unit)
74 | }
75 |
76 | /**
77 | * Console for logging
78 | */
79 | @JsName("console")
80 | external object WasmConsole : JsAny {
81 | fun log(message: String)
82 | fun error(message: String)
83 | fun warn(message: String)
84 | }
85 |
86 | /**
87 | * Helper to create JS array of strings
88 | */
89 | fun jsArrayOf(vararg elements: String): JsArray {
90 | val array = JsArray()
91 | elements.forEach { array[array.length] = it.toJsString() }
92 | return array
93 | }
94 |
95 | /**
96 | * Helper extension to convert JsArray to Kotlin List
97 | */
98 | fun JsArray.toList(): List {
99 | val result = mutableListOf()
100 | for (i in 0 until this.length) {
101 | val item = this[i]
102 | if (item != null) {
103 | result.add(item)
104 | }
105 | }
106 | return result
107 | }
108 |
109 | // Create empty config template at top-level - js() call must be single expression
110 | private val _emptyJsObject: JsAny = js("({})")
111 |
112 | /**
113 | * Create a Module configuration object for wasm-git
114 | */
115 | fun createModuleConfig(
116 | onPrint: ((String) -> Unit)? = null,
117 | onPrintErr: ((String) -> Unit)? = null,
118 | onRuntimeInitialized: (() -> Unit)? = null
119 | ): ModuleConfig {
120 | // Cast and set properties
121 | val config = _emptyJsObject.unsafeCast()
122 | if (onPrint != null) config.print = onPrint
123 | if (onPrintErr != null) config.printErr = onPrintErr
124 | if (onRuntimeInitialized != null) config.onRuntimeInitialized = onRuntimeInitialized
125 | return config
126 | }
127 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [](https://github.com/JetBrains#jetbrains-on-github)
3 |
4 | This example showcases a web application built with [Compose Multiplatform for web](#compose-multiplatform-for-web) and [Kotlin/Wasm](https://kotl.in/wasm).
5 |
6 | Check it out:
7 |
8 | [](https://kotlin.github.io/kotlin-wasm-compose-template/).
9 |
10 | 
11 |
12 | ## Compose Multiplatform for web
13 |
14 | > **Note:**
15 | > Web support is an [Alpha](https://kotlinlang.org/docs/components-stability.html) feature. It may be changed at any time.
16 | > You can use it in scenarios before production. We would appreciate your feedback on [GitHub](https://github.com/JetBrains/compose-multiplatform/issues).
17 | >
18 | > [Join the compose-web community](https://slack-chats.kotlinlang.org/c/compose-web).
19 |
20 | Compose Multiplatform for web enables sharing your mobile or desktop UIs on the web.
21 |
22 | Compose Multiplatform for web is based on [Kotlin/Wasm](https://kotl.in/wasm), the newest target for Kotlin Multiplatform projects.
23 | This enables running your code in the browser, leveraging WebAssembly's advantages like high and consistent application performance.
24 |
25 | Follow the instructions in the sections below to try out this application built with Compose Multiplatform for web and Kotlin/Wasm.
26 |
27 | ## Set up the environment
28 |
29 | Before starting, ensure you have the necessary IDE and browser setup to run the application.
30 |
31 | ### IDE
32 |
33 | We recommend using [IntelliJ IDEA 2023.1 or later](https://www.jetbrains.com/idea/) to work with the project.
34 | It supports Kotlin/Wasm out of the box.
35 |
36 | ### Browser (for Kotlin/Wasm target)
37 |
38 | To run applications built with Kotlin/Wasm in a browser, you need a browser supporting the [Wasm Garbage Collection (GC) feature](https://github.com/WebAssembly/gc):
39 |
40 | **Chrome and Chromium-based**
41 |
42 | * **For version 119 or later:**
43 |
44 | Works by default.
45 |
46 | **Firefox**
47 |
48 | * **For version 120 or later:**
49 |
50 | Works by default.
51 |
52 | **Safari/WebKit**
53 |
54 | Wasm GC support is currently under
55 | [active development](https://bugs.webkit.org/show_bug.cgi?id=247394).
56 |
57 | > **Note:**
58 | > For more information about the browser versions, see the [Troubleshooting documentation](https://kotl.in/wasm_help/).
59 |
60 | ## Build and run
61 |
62 | To build and run the application:
63 |
64 | 1. In IntelliJ IDEA, open the repository.
65 | 2. Run the application by typing one of the following Gradle commands in the terminal:
66 |
67 | * **Web version:**
68 |
69 | `./gradlew :composeApp:wasmJsBrowserProductionRun`
70 |
71 |
72 | Once the application starts, open the following URL in your browser:
73 |
74 | `http://localhost:8080`
75 |
76 | > **Note:**
77 | > The port number can vary. If the port 8080 is unavailable, you can find the corresponding port number printed in the console
78 | > after building the application.
79 |
80 |
81 | * **Desktop version:**
82 |
83 | `./gradlew :composeApp:run`
84 |
85 | ## Feedback and questions
86 |
87 | Give it a try and share your feedback or questions in our [#compose-web](https://slack-chats.kotlinlang.org/c/compose-web) Slack channel.
88 | [Get a Slack invite](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up).
89 | You can also share your comments with [@bashorov](https://twitter.com/bashorov) on X (Twitter).
90 |
91 | ## Learn more
92 |
93 | * [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/#compose-multiplatform)
94 | * [Kotlin/Wasm](https://kotl.in/wasm/)
95 | * [Other Kotlin/Wasm examples](https://github.com/Kotlin/kotlin-wasm-examples/tree/main)
96 |
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Compose App
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/web/libgit2_webworker.js:
--------------------------------------------------------------------------------
1 | var Module = {
2 | locateFile: function(s) {
3 | return 'https://unpkg.com/wasm-git@0.0.2/' + s;
4 | },
5 | 'print': function(text) {
6 |
7 | console.log(text);
8 | postMessage({'stdout': text});
9 | },
10 | 'printErr': function(text) {
11 |
12 | console.error(text);
13 | postMessage({'stderr': text});
14 | }
15 | };
16 |
17 | let accessToken = 'ANONYMOUS';
18 | XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open;
19 | XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
20 | this._open(method, url, async, user, password);
21 | this.setRequestHeader('Authorization', `Bearer ${accessToken}`);
22 | }
23 | importScripts('https://unpkg.com/wasm-git@0.0.2/lg2.js');
24 |
25 | function writeGlobalConfig(username, useremail) {
26 | FS.writeFile('/home/web_user/.gitconfig', '[user]\n' +
27 | `name = ${username}\n` +
28 | `email = ${useremail}`);
29 |
30 | }
31 |
32 | const lgPromise = new Promise(resolve => {
33 | Module.onRuntimeInitialized = () => {
34 | writeGlobalConfig('Test user', 'test@example.com');
35 | resolve(Module);
36 | }
37 | });
38 |
39 | let currentRepoRootDir;
40 |
41 | onmessage = async (msg) => {
42 | const lg = await lgPromise;
43 | if (msg.data.accessToken) {
44 | accessToken = msg.data.accessToken;
45 | writeGlobalConfig(msg.data.username, msg.data.useremail);
46 | postMessage({accessTokenConfigured: true})
47 | } else if (msg.data.command === 'writeandcommit') {
48 | FS.writeFile(msg.data.filename, msg.data.contents);
49 | lg.callMain(['add', '--verbose', msg.data.filename]);
50 | lg.callMain(['commit','-m', msg.data.commitmessage]);
51 | FS.syncfs(false, () => {
52 | console.log(currentRepoRootDir, 'stored to indexeddb');
53 | postMessage({ dircontents: FS.readdir('.') });
54 | });
55 | } else if (msg.data.command === 'push') {
56 | lg.callMain(['push']);
57 | FS.syncfs(false, () => {
58 | console.log(currentRepoRootDir, 'stored to indexeddb');
59 | postMessage({ dircontents: FS.readdir('.') });
60 | });
61 | } else if (msg.data.command === 'synclocal') {
62 | currentRepoRootDir = msg.data.url.substring(msg.data.url.lastIndexOf('/') + 1);
63 | console.log('synclocal', currentRepoRootDir);
64 |
65 | FS.mkdir(`/${currentRepoRootDir}`);
66 | FS.mount(IDBFS, { }, `/${currentRepoRootDir}`);
67 |
68 | FS.syncfs(true, () => {
69 |
70 | if( FS.readdir(`/${currentRepoRootDir}`).find(file => file === '.git') ) {
71 | FS.chdir( `/${currentRepoRootDir}` );
72 | postMessage({ dircontents: FS.readdir('.') });
73 | console.log(currentRepoRootDir, 'restored from indexeddb');
74 | } else {
75 | FS.chdir( '/' );
76 | console.log('no git repo in', currentRepoRootDir);
77 | }
78 | });
79 | } else if (msg.data.command === 'deletelocal') {
80 |
81 | FS.unmount(`/${currentRepoRootDir}`);
82 | console.log('deleting database', currentRepoRootDir);
83 | self.indexedDB.deleteDatabase('/' + currentRepoRootDir);
84 | postMessage({ deleted: currentRepoRootDir});
85 | } else if (msg.data.command === 'clone') {
86 | currentRepoRootDir = msg.data.url.substring(msg.data.url.lastIndexOf('/') + 1);
87 |
88 | postMessage({stdout: `git clone ${msg.data.url}`});
89 | lg.callMain(['clone', msg.data.url, currentRepoRootDir]);
90 | FS.chdir(currentRepoRootDir);
91 |
92 | FS.syncfs(false, () => {
93 | console.log(currentRepoRootDir, 'stored to indexeddb');
94 | });
95 | postMessage({ dircontents: FS.readdir('.') });
96 | } else if (msg.data.command === 'pull') {
97 | lg.callMain(['fetch', 'origin']);
98 | lg.callMain(['merge', 'origin/master']);
99 | FS.syncfs(false, () => {
100 | console.log(currentRepoRootDir, 'stored to indexeddb');
101 | });
102 | } else if (msg.data.command === 'readfile') {
103 | try {
104 | postMessage({
105 | filename: msg.data.filename,
106 | filecontents: FS.readFile(msg.data.filename, {encoding: 'utf8'})
107 | });
108 | } catch (e) {
109 | postMessage({'stderr': JSON.stringify(e)});
110 | }
111 | } else {
112 | lg.callMain([msg.data.command]);
113 | }
114 | };
--------------------------------------------------------------------------------
/composeApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | @file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
2 |
3 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat
4 | import org.jetbrains.compose.ExperimentalComposeLibrary
5 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
6 |
7 | plugins {
8 | alias(libs.plugins.kotlinMultiplatform)
9 | alias(libs.plugins.androidApplication)
10 | alias(libs.plugins.jetbrainsCompose)
11 | alias(libs.plugins.pluginCompose)
12 | }
13 |
14 | kotlin {
15 | androidTarget {
16 | compilerOptions {
17 | jvmTarget.set(JvmTarget.JVM_18)
18 | }
19 | }
20 |
21 | jvm("desktop")
22 |
23 | listOf(
24 | iosX64(),
25 | iosArm64(),
26 | iosSimulatorArm64()
27 | ).forEach { iosTarget ->
28 | iosTarget.binaries.framework {
29 | baseName = "ComposeApp"
30 | isStatic = true
31 | }
32 | }
33 |
34 | wasmJs {
35 | outputModuleName = "composeApp"
36 | browser {
37 | commonWebpackConfig {
38 | cssSupport {
39 | enabled.set(true)
40 | }
41 | }
42 | }
43 | binaries.executable()
44 | }
45 |
46 | sourceSets {
47 | val desktopMain by getting
48 |
49 | androidMain.dependencies {
50 | implementation(libs.compose.ui.tooling.preview)
51 | implementation(libs.androidx.activity.compose)
52 | }
53 | desktopMain.dependencies {
54 | implementation(compose.desktop.currentOs)
55 | }
56 | commonMain.dependencies {
57 | implementation(compose.runtime)
58 | implementation(compose.foundation)
59 | implementation(compose.material)
60 | implementation(compose.ui)
61 | @OptIn(ExperimentalComposeLibrary::class)
62 | implementation(compose.components.resources)
63 |
64 | // Koog AI Framework - JVM only for now
65 | implementation("ai.koog:koog-agents:0.5.2")
66 | // Koog needs these executors
67 | implementation("ai.koog:prompt-executor-llms-all:0.5.2")
68 | }
69 | wasmJsMain.dependencies {
70 | implementation(compose.components.resources)
71 |
72 | implementation(npm("wasm-git", "0.0.13"))
73 | // web-tree-sitter for JS platform
74 | implementation(npm("web-tree-sitter", "0.22.2"))
75 | // TreeSitter WASM artifacts - matching autodev-workbench versions
76 | implementation(npm("@unit-mesh/treesitter-artifacts", "1.7.4"))
77 | // Copy webpack plugin to copy WASM files
78 | implementation(devNpm("copy-webpack-plugin", "12.0.2"))
79 | }
80 | }
81 | }
82 |
83 | android {
84 | namespace = "org.example.project"
85 | compileSdk = libs.versions.android.compileSdk.get().toInt()
86 |
87 | sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
88 | sourceSets["main"].res.srcDirs("src/androidMain/res")
89 | sourceSets["main"].resources.srcDirs("src/commonMain/composeResources")
90 |
91 | defaultConfig {
92 | applicationId = "org.example.project"
93 | minSdk = libs.versions.android.minSdk.get().toInt()
94 | targetSdk = libs.versions.android.targetSdk.get().toInt()
95 | versionCode = 1
96 | versionName = "1.0"
97 | }
98 | packaging {
99 | resources {
100 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
101 | }
102 | }
103 | buildTypes {
104 | getByName("release") {
105 | isMinifyEnabled = false
106 | }
107 | }
108 | compileOptions {
109 | sourceCompatibility = JavaVersion.VERSION_1_8
110 | targetCompatibility = JavaVersion.VERSION_1_8
111 | }
112 | dependencies {
113 | debugImplementation(libs.compose.ui.tooling)
114 | }
115 | }
116 |
117 | compose.desktop {
118 | application {
119 | mainClass = "MainKt"
120 |
121 | nativeDistributions {
122 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
123 | packageName = "org.example.project"
124 | packageVersion = "1.0.0"
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
14 |
18 |
24 |
30 |
36 |
37 |
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/main.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.ExperimentalComposeUiApi
2 | import androidx.compose.ui.window.CanvasBasedWindow
3 | import kotlinx.coroutines.await
4 | import kotlinx.coroutines.launch
5 | import kotlinx.coroutines.GlobalScope
6 | import kotlin.js.Promise
7 |
8 | @OptIn(ExperimentalComposeUiApi::class)
9 | suspend fun main() {
10 | suspend fun createGit(): LibGit2Module {
11 | val config = createModuleConfig(
12 | onPrint = { text ->
13 | console.log("[Git Output] $text")
14 | },
15 | onPrintErr = { text ->
16 | console.error("[Git Error] $text")
17 | },
18 | )
19 |
20 | val git: LibGit2Module = lg2(config).await()
21 | debugGit(git)
22 | return git
23 | }
24 |
25 | suspend fun executeGit() {
26 | val git: LibGit2Module = createGit()
27 |
28 | // Create GitRepository instance
29 | val repo = GitRepository(git)
30 |
31 | // Configure git user
32 | repo.configureUser("Test User", "test@example.com")
33 |
34 | // Example: Clone a repository
35 | console.log("=== Starting Git Clone Demo ===")
36 | try {
37 | val files = repo.clone("https://github.com/phodal-archive/mini-file")
38 | console.log("Repository cloned successfully!")
39 | console.log("Files in repository: ${files.joinToString(", ")}")
40 |
41 | // Execute git status
42 | console.log("\n=== Git Status ===")
43 | console.log("Executing: git status")
44 | try {
45 | repo.status()
46 | } catch (e: Throwable) {
47 | console.warn("Status error: ${e.message}")
48 | }
49 |
50 | // Execute git log (show last 5 commits)
51 | console.log("\n=== Git Log (last 5 commits) ===")
52 | try {
53 | repo.log("--oneline", "-5")
54 | } catch (e: Throwable) {
55 | console.warn("Log error: ${e.message}")
56 | }
57 |
58 | // Example: Read a file (adjust path based on actual repo structure)
59 | console.log("\n=== README.md content ===")
60 | try {
61 | val readme = repo.readFile("README.md")
62 | console.log(readme.take(200) + "...") // Show first 200 chars
63 | } catch (e: Throwable) {
64 | console.warn("README.md not found or couldn't be read: ${e.message}")
65 | }
66 |
67 | // Example: Execute git diff (if there are any changes)
68 | console.log("\n=== Git Diff ===")
69 | repo.diff()
70 | } catch (e: Throwable) {
71 | console.error("Git operation failed: ${e.message}")
72 | e.printStackTrace()
73 | }
74 | }
75 |
76 | suspend fun initialize() {
77 |
78 | val git: LibGit2Module = createGit()
79 | console.log("creating file")
80 | git.FS.writeFile("demo.txt", "Hello World!")
81 | console.log("created file")
82 | val fileArray: Array = git.FS.readFile("demo.txt").toArray()
83 | val bytes = fileArray.map { it.toInt().toByte() }.toByteArray()
84 | val content = bytes.decodeToString()
85 | console.log(content)
86 |
87 | // Initialize TreeSitter (original code)
88 | val initPromise: Promise = WebTreeSitter.Parser.init().unsafeCast()
89 | initPromise.await()
90 |
91 | val loadPromise: Promise =
92 | WebTreeSitter.Parser.Language.load(CodeLanguage.JAVA.getWasmPath()).unsafeCast()
93 | val language: WebTreeSitter.Parser.Language = loadPromise.await()
94 |
95 | val parser = WebTreeSitter.Parser()
96 | parser.setLanguage(language)
97 |
98 | val javaCode = """
99 | public class HelloWorld {
100 | public static void main(String[] args) {
101 | System.out.println("Hello, World!");
102 | }
103 |
104 | private void greet(String name) {
105 | System.out.println("Hello, " + name);
106 | }
107 | }
108 | """.trimIndent()
109 |
110 | val tree = parser.parse(javaCode)
111 | debugNode(tree.rootNode)
112 |
113 | val queryObj = language.query("(class_declaration (_) @classBody)")
114 | debug(queryObj)
115 | val captures = queryObj.captures(tree.rootNode)
116 | captures.toList().forEach {
117 | println(it.node.type)
118 | println(it.node.text)
119 | }
120 | }
121 |
122 | initialize()
123 |
124 | CanvasBasedWindow(canvasElementId = "ComposeTarget") {
125 | ParserDemo()
126 | }
127 | }
128 |
129 | fun debug(queryObj: Query): Unit = js("""console.log(queryObj)""")
130 | fun debugNode(queryObj: SyntaxNode): Unit = js("""console.log(queryObj)""")
131 | fun debugGit(git: LibGit2Module): Unit = js("""console.log(git)""")
132 |
--------------------------------------------------------------------------------
/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/GitRepository.kt:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.await
2 | import kotlin.js.Promise
3 |
4 | /**
5 | * Git Repository wrapper for wasm-git operations
6 | */
7 | class GitRepository(private val git: LibGit2Module) {
8 | private var currentRepoRootDir: String? = null
9 |
10 | /**
11 | * Configure global git user settings
12 | */
13 | fun configureUser(username: String, email: String) {
14 | git.FS.writeFile(
15 | "/home/web_user/.gitconfig",
16 | "[user]\nname = $username\nemail = $email"
17 | )
18 | console.log("Git user configured: $username <$email>")
19 | }
20 |
21 | /**
22 | * Clone a repository
23 | * @param url Repository URL (e.g., "https://github.com/unit-mesh/untitled")
24 | * @return List of files in the cloned repository
25 | */
26 | suspend fun clone(url: String): List {
27 | val url = "https://cors-anywhere.com/$url"
28 | currentRepoRootDir = url.substringAfterLast('/')
29 | console.log("Cloning $url into $currentRepoRootDir...")
30 | val exitCode = git.callMain(jsArrayOf("clone", url, currentRepoRootDir!!)).await().toInt()
31 | console.log("End clone with exit code: $exitCode")
32 | if (exitCode != 0) {
33 | console.error("Clone failed with exit code: $exitCode")
34 | throw Exception("Git clone failed with exit code: $exitCode")
35 | }
36 |
37 | console.log("Start change directory to $currentRepoRootDir")
38 | git.FS.chdir(currentRepoRootDir!!)
39 | console.log("Changed directory to $currentRepoRootDir")
40 |
41 | val files = readDir(".")
42 | console.log("Clone completed. Files: ${files.joinToString(", ")}")
43 | return files
44 | }
45 |
46 | /**
47 | * Execute git diff command
48 | * @param args Optional arguments (e.g., file path, commit refs)
49 | * @return Exit code (0 for success)
50 | */
51 | suspend fun diff(vararg args: String): Int {
52 | val allArgs = listOf("diff") + args.toList()
53 | console.log("Executing: git ${allArgs.joinToString(" ")}")
54 | return git.callMain(jsArrayOf(*allArgs.toTypedArray())).await().toInt()
55 | }
56 |
57 | /**
58 | * Execute git status
59 | */
60 | suspend fun status(): Int {
61 | console.log("Executing: git status")
62 | return git.callMain(jsArrayOf("status")).await().toInt()
63 | }
64 |
65 | /**
66 | * Execute git log
67 | * @param args Optional arguments (e.g., "--oneline", "-10")
68 | */
69 | suspend fun log(vararg args: String): Int {
70 | val allArgs = listOf("log") + args.toList()
71 | console.log("Executing: git ${allArgs.joinToString(" ")}")
72 | return git.callMain(jsArrayOf(*allArgs.toTypedArray())).await().toInt()
73 | }
74 |
75 | /**
76 | * Execute git add
77 | * @param files Files to add
78 | */
79 | suspend fun add(vararg files: String): Int {
80 | val allArgs = listOf("add") + files.toList()
81 | console.log("Executing: git ${allArgs.joinToString(" ")}")
82 | return git.callMain(jsArrayOf(*allArgs.toTypedArray())).await().toInt()
83 | }
84 |
85 | /**
86 | * Execute git commit
87 | * @param message Commit message
88 | */
89 | suspend fun commit(message: String): Int {
90 | console.log("Executing: git commit -m \"$message\"")
91 | return git.callMain(jsArrayOf("commit", "-m", message)).await().toInt()
92 | }
93 |
94 | /**
95 | * Execute git pull
96 | */
97 | suspend fun pull(): Int {
98 | console.log("Executing: git pull")
99 | return git.callMain(jsArrayOf("pull", "origin")).await().toInt()
100 | }
101 |
102 | /**
103 | * Execute git push
104 | */
105 | suspend fun push(): Int {
106 | console.log("Executing: git push")
107 | return git.callMain(jsArrayOf("push")).await().toInt()
108 | }
109 |
110 | /**
111 | * Execute git branch
112 | * @param args Optional arguments (e.g., branch name)
113 | */
114 | suspend fun branch(vararg args: String): Int {
115 | val allArgs = mutableListOf("branch")
116 | allArgs.addAll(args.toList())
117 | console.log("Executing: git ${allArgs.joinToString(" ")}")
118 | return git.callMain(jsArrayOf(*allArgs.toTypedArray())).await().toInt()
119 | }
120 |
121 | /**
122 | * List all branches (alternative to branch -a)
123 | */
124 | suspend fun listBranches(): Int {
125 | console.log("Executing: git branch --list")
126 | return git.callMain(jsArrayOf("branch", "--list")).await().toInt()
127 | }
128 |
129 | /**
130 | * Execute git checkout
131 | * @param branch Branch name or commit hash
132 | */
133 | suspend fun checkout(branch: String): Int {
134 | console.log("Executing: git checkout $branch")
135 | return git.callMain(jsArrayOf("checkout", branch)).await().toInt()
136 | }
137 |
138 | /**
139 | * Read a file from the repository
140 | * @param path File path relative to repository root
141 | */
142 | fun readFile(path: String): String {
143 | try {
144 | return git.FS.readFile(path).toString()
145 | } catch (e: Throwable) {
146 | console.error("Failed to read file $path: ${e.message}")
147 | throw e
148 | }
149 | }
150 |
151 | /**
152 | * Write a file to the repository
153 | * @param path File path relative to repository root
154 | * @param content File content
155 | */
156 | fun writeFile(path: String, content: String) {
157 | git.FS.writeFile(path, content)
158 | console.log("Written file: $path")
159 | }
160 |
161 | /**
162 | * Read directory contents
163 | * @param path Directory path
164 | */
165 | fun readDir(path: String): List {
166 | val jsArray = git.FS.readdir(path)
167 | return (0 until jsArray.length).map { jsArray[it].toString() }
168 | }
169 |
170 | /**
171 | * Execute any git command
172 | * @param command Command and arguments
173 | *
174 | */
175 | suspend fun execute(vararg command: String): Int {
176 | return git.callMain(jsArrayOf(*command)).await().toInt()
177 | }
178 |
179 | /**
180 | * Get current repository root directory
181 | */
182 | fun getCurrentRepo(): String? = currentRepoRootDir
183 |
184 | /**
185 | * Get current branch name
186 | * TODO: symbolic-ref not supporting
187 | */
188 | suspend fun getCurrentBranch(): String? {
189 | return try {
190 | // Try to get current branch using symbolic-ref
191 | val exitCode = git.callMain(jsArrayOf("symbolic-ref", "--short", "HEAD")).await().toInt()
192 | if (exitCode == 0) {
193 | // Successfully got branch name
194 | null // Output goes to console
195 | } else {
196 | null
197 | }
198 | } catch (e: Throwable) {
199 | console.warn("Could not determine current branch: ${e.message}")
200 | null
201 | }
202 | }
203 |
204 | /**
205 | * Check if file exists
206 | */
207 | fun fileExists(path: String): Boolean {
208 | return try {
209 | readFile(path)
210 | true
211 | } catch (e: Throwable) {
212 | false
213 | }
214 | }
215 | }
216 |
217 | /**
218 | * Console object for Kotlin/Wasm
219 | */
220 | external object console : JsAny {
221 | fun log(message: String)
222 | fun error(message: String)
223 | fun warn(message: String)
224 | }
225 |
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/JavaParser.kt:
--------------------------------------------------------------------------------
1 | import kotlinx.coroutines.await
2 | import kotlin.js.Promise
3 |
4 | /**
5 | * Java parser using Tree-sitter for parsing Java code
6 | */
7 | class JavaParser {
8 | private var parser: WebTreeSitter.Parser? = null
9 | private var language: WebTreeSitter.Parser.Language? = null
10 | private var isInitialized = false
11 |
12 | /**
13 | * Initialize the parser with Java language
14 | */
15 | suspend fun initialize() {
16 | if (isInitialized) return
17 |
18 | try {
19 | // Initialize Tree-sitter
20 | val initPromise: Promise = WebTreeSitter.Parser.init().unsafeCast()
21 | initPromise.await()
22 |
23 | // Load Java language
24 | val loadPromise: Promise =
25 | WebTreeSitter.Parser.Language.load(CodeLanguage.JAVA.getWasmPath()).unsafeCast()
26 | language = loadPromise.await()
27 |
28 | // Create parser and set language
29 | parser = WebTreeSitter.Parser()
30 | parser?.setLanguage(language)
31 |
32 | isInitialized = true
33 | } catch (e: Exception) {
34 | throw Exception("Failed to initialize Java parser: ${e.message}", e)
35 | }
36 | }
37 |
38 | /**
39 | * Parse Java code and return the syntax tree
40 | */
41 | private fun parseCode(javaCode: String): Tree {
42 | if (!isInitialized) {
43 | throw IllegalStateException("Parser not initialized. Call initialize() first.")
44 | }
45 |
46 | return parser?.parse(javaCode)
47 | ?: throw IllegalStateException("Parser not properly initialized")
48 | }
49 |
50 | /**
51 | * Parse Java source code and return the syntax tree
52 | */
53 | fun parse(sourceCode: String): TreeSitterTree? {
54 | return parseCode(sourceCode).unsafeCast()
55 | }
56 |
57 | /**
58 | * Parse Java source code and return a formatted string representation
59 | */
60 | fun parseToString(sourceCode: String): String {
61 | val tree = parseCode(sourceCode)
62 | return formatNode(tree.rootNode, 0)
63 | }
64 |
65 | /**
66 | * Extract all method names from Java source code
67 | */
68 | fun extractMethodNames(sourceCode: String): List {
69 | val tree = parseCode(sourceCode)
70 | val query = language?.query("""
71 | (method_declaration
72 | name: (identifier) @method.name)
73 | (constructor_declaration
74 | name: (identifier) @method.name)
75 | """.trimIndent()) ?: throw IllegalStateException("Language not initialized")
76 |
77 | val captures = query.captures(tree.rootNode).toArray()
78 | val methodNames = mutableListOf()
79 |
80 | for (capture in captures) {
81 | if (capture.name == "method.name") {
82 | methodNames.add(capture.node.text)
83 | }
84 | }
85 |
86 | query.delete()
87 | return methodNames.distinct()
88 | }
89 |
90 | /**
91 | * Extract all class names from Java source code
92 | */
93 | fun extractClassNames(sourceCode: String): List {
94 | val tree = parseCode(sourceCode)
95 | val query = language?.query("(class_declaration name: (identifier) @class.name)")
96 | ?: throw IllegalStateException("Language not initialized")
97 |
98 | val captures = query.captures(tree.rootNode).toArray()
99 | val classNames = mutableListOf()
100 |
101 | for (capture in captures) {
102 | if (capture.name == "class.name") {
103 | classNames.add(capture.node.text)
104 | }
105 | }
106 |
107 | query.delete()
108 | return classNames.distinct()
109 | }
110 |
111 | /**
112 | * Extract field names from Java code
113 | */
114 | fun extractFieldNames(sourceCode: String): List {
115 | val tree = parseCode(sourceCode)
116 | val query = language?.query("""
117 | (field_declaration
118 | declarator: (variable_declarator
119 | name: (identifier) @field.name))
120 | """.trimIndent()) ?: throw IllegalStateException("Language not initialized")
121 |
122 | val captures = query.captures(tree.rootNode).toArray()
123 | val fieldNames = mutableListOf()
124 |
125 | for (capture in captures) {
126 | if (capture.name == "field.name") {
127 | fieldNames.add(capture.node.text)
128 | }
129 | }
130 |
131 | query.delete()
132 | return fieldNames.distinct()
133 | }
134 |
135 | /**
136 | * Extract import statements from Java code
137 | */
138 | fun extractImports(sourceCode: String): List {
139 | val tree = parseCode(sourceCode)
140 | val query = language?.query("(import_declaration (scoped_identifier) @import.name)")
141 | ?: throw IllegalStateException("Language not initialized")
142 |
143 | val captures = query.captures(tree.rootNode).toArray()
144 | val imports = mutableListOf()
145 |
146 | for (capture in captures) {
147 | if (capture.name == "import.name") {
148 | imports.add(capture.node.text)
149 | }
150 | }
151 |
152 | query.delete()
153 | return imports.distinct()
154 | }
155 |
156 | /**
157 | * Get package declaration from Java code
158 | */
159 | fun getPackageName(sourceCode: String): String? {
160 | val tree = parseCode(sourceCode)
161 | val query = language?.query("(package_declaration (scoped_identifier) @package.name)")
162 | ?: throw IllegalStateException("Language not initialized")
163 |
164 | val captures = query.captures(tree.rootNode).toArray()
165 | var packageName: String? = null
166 |
167 | for (capture in captures) {
168 | if (capture.name == "package.name") {
169 | packageName = capture.node.text
170 | break
171 | }
172 | }
173 |
174 | query.delete()
175 | return packageName
176 | }
177 |
178 | /**
179 | * Find all string literals in the code
180 | */
181 | fun extractStringLiterals(sourceCode: String): List {
182 | val tree = parseCode(sourceCode)
183 | val query = language?.query("(string_literal) @string")
184 | ?: throw IllegalStateException("Language not initialized")
185 |
186 | val captures = query.captures(tree.rootNode).toArray()
187 | val strings = mutableListOf()
188 |
189 | for (capture in captures) {
190 | if (capture.name == "string") {
191 | strings.add(capture.node.text)
192 | }
193 | }
194 |
195 | query.delete()
196 | return strings
197 | }
198 |
199 | /**
200 | * Check if code contains syntax errors
201 | */
202 | fun hasSyntaxErrors(sourceCode: String): Boolean {
203 | val tree = parseCode(sourceCode)
204 | return tree.rootNode.hasError || tree.rootNode.isError
205 | }
206 |
207 | /**
208 | * Format a node and its children as a readable string
209 | */
210 | private fun formatNode(node: SyntaxNode, depth: Int): String {
211 | val indent = " ".repeat(depth)
212 | val result = StringBuilder()
213 |
214 | result.appendLine("${indent}${node.type} [${node.startPosition.row}:${node.startPosition.column} - ${node.endPosition.row}:${node.endPosition.column}]")
215 |
216 | if (node.text.isNotEmpty() && node.isNamed) {
217 | val textPreview = node.text.take(50).replace("\n", "\\n")
218 | if (node.text.length > 50) {
219 | result.appendLine("${indent} Text: \"$textPreview...\"")
220 | } else {
221 | result.appendLine("${indent} Text: \"$textPreview\"")
222 | }
223 | }
224 |
225 | for (child in node.children.toArray()) {
226 | result.append(formatNode(child, depth + 1))
227 | }
228 |
229 | return result.toString()
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/ParserDemo.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.foundation.layout.*
2 | import androidx.compose.foundation.rememberScrollState
3 | import androidx.compose.foundation.verticalScroll
4 | import androidx.compose.material.*
5 | import androidx.compose.runtime.*
6 | import androidx.compose.ui.Alignment
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.text.font.FontFamily
9 | import androidx.compose.ui.unit.dp
10 | import kotlinx.coroutines.launch
11 |
12 | @Composable
13 | fun ParserDemo() {
14 | var parserOutput by remember { mutableStateOf("Click 'Parse Java Code' to see the result") }
15 | var isLoading by remember { mutableStateOf(false) }
16 | var javaCode by remember {
17 | mutableStateOf("""
18 | public class HelloWorld {
19 | public static void main(String[] args) {
20 | System.out.println("Hello, World!");
21 | }
22 |
23 | private void greet(String name) {
24 | System.out.println("Hello, " + name);
25 | }
26 |
27 | public int calculate(int a, int b) {
28 | return a + b;
29 | }
30 | }
31 | """.trimIndent())
32 | }
33 |
34 | val scope = rememberCoroutineScope()
35 |
36 | MaterialTheme {
37 | Column(
38 | modifier = Modifier
39 | .fillMaxSize()
40 | .padding(16.dp),
41 | horizontalAlignment = Alignment.CenterHorizontally
42 | ) {
43 | Text(
44 | "Java Parser Demo",
45 | style = MaterialTheme.typography.h4,
46 | modifier = Modifier.padding(bottom = 16.dp)
47 | )
48 |
49 | Text(
50 | "Edit the Java code below and click 'Parse' to analyze it:",
51 | style = MaterialTheme.typography.body1,
52 | modifier = Modifier.padding(bottom = 8.dp)
53 | )
54 |
55 | // Java code input
56 | OutlinedTextField(
57 | value = javaCode,
58 | onValueChange = { javaCode = it },
59 | modifier = Modifier
60 | .fillMaxWidth()
61 | .height(200.dp),
62 | label = { Text("Java Code") },
63 | textStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
64 | )
65 |
66 | Spacer(modifier = Modifier.height(16.dp))
67 |
68 | // Parse button
69 | Button(
70 | onClick = {
71 | scope.launch {
72 | isLoading = true
73 | try {
74 | val parser = JavaParser()
75 | parser.initialize()
76 |
77 | val result = StringBuilder()
78 | result.appendLine("=== Java Code Analysis ===\n")
79 |
80 | // Check for syntax errors first
81 | if (parser.hasSyntaxErrors(javaCode)) {
82 | result.appendLine("⚠️ Syntax errors detected in the code!\n")
83 | }
84 |
85 | // Extract package name
86 | val packageName = parser.getPackageName(javaCode)
87 | if (packageName != null) {
88 | result.appendLine("📁 Package: $packageName")
89 | result.appendLine()
90 | }
91 |
92 | // Extract import statements
93 | val imports = parser.extractImports(javaCode)
94 | if (imports.isNotEmpty()) {
95 | result.appendLine("📥 Imports:")
96 | imports.forEach { import ->
97 | result.appendLine(" - $import")
98 | }
99 | result.appendLine()
100 | }
101 |
102 | // Extract class names
103 | val classNames = parser.extractClassNames(javaCode)
104 | if (classNames.isNotEmpty()) {
105 | result.appendLine("📦 Classes found: ${classNames.joinToString(", ")}")
106 | result.appendLine()
107 | }
108 |
109 | // Extract method names
110 | val methodNames = parser.extractMethodNames(javaCode)
111 | if (methodNames.isNotEmpty()) {
112 | result.appendLine("🔧 Methods found: ${methodNames.joinToString(", ")}")
113 | result.appendLine()
114 | }
115 |
116 | // Extract field names
117 | val fieldNames = parser.extractFieldNames(javaCode)
118 | if (fieldNames.isNotEmpty()) {
119 | result.appendLine("🏷️ Fields found: ${fieldNames.joinToString(", ")}")
120 | result.appendLine()
121 | }
122 |
123 | // Extract string literals
124 | val stringLiterals = parser.extractStringLiterals(javaCode)
125 | if (stringLiterals.isNotEmpty()) {
126 | result.appendLine("💬 String literals: ${stringLiterals.size} found")
127 | stringLiterals.take(5).forEach { str ->
128 | result.appendLine(" - $str")
129 | }
130 | if (stringLiterals.size > 5) {
131 | result.appendLine(" ... and ${stringLiterals.size - 5} more")
132 | }
133 | result.appendLine()
134 | }
135 |
136 | // Show truncated syntax tree
137 | result.appendLine("🌳 Syntax Tree (first 500 characters):")
138 | val syntaxTree = parser.parseToString(javaCode)
139 | if (syntaxTree.length > 500) {
140 | result.appendLine(syntaxTree.take(500) + "\n... (truncated)")
141 | } else {
142 | result.appendLine(syntaxTree)
143 | }
144 |
145 | parserOutput = result.toString()
146 | } catch (e: Exception) {
147 | parserOutput = "Error: ${e.message}\n\nStack trace: ${e.stackTraceToString()}"
148 | } finally {
149 | isLoading = false
150 | }
151 | }
152 | },
153 | enabled = !isLoading,
154 | modifier = Modifier.padding(8.dp)
155 | ) {
156 | if (isLoading) {
157 | CircularProgressIndicator(
158 | modifier = Modifier.size(20.dp),
159 | color = MaterialTheme.colors.onPrimary
160 | )
161 | Spacer(modifier = Modifier.width(8.dp))
162 | }
163 | Text(if (isLoading) "Parsing..." else "Parse Java Code")
164 | }
165 |
166 | Spacer(modifier = Modifier.height(16.dp))
167 |
168 | // Output display
169 | Card(
170 | modifier = Modifier
171 | .fillMaxWidth()
172 | .weight(1f),
173 | elevation = 4.dp
174 | ) {
175 | Column(
176 | modifier = Modifier
177 | .padding(16.dp)
178 | .verticalScroll(rememberScrollState())
179 | ) {
180 | Text(
181 | "Output:",
182 | style = MaterialTheme.typography.h6,
183 | modifier = Modifier.padding(bottom = 8.dp)
184 | )
185 | Text(
186 | parserOutput,
187 | style = MaterialTheme.typography.body2.copy(fontFamily = FontFamily.Monospace)
188 | )
189 | }
190 | }
191 | }
192 | }
193 | }
194 |
195 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
11 | 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
12 | 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
13 | 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXFileReference section */
17 | 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
18 | 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
19 | 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; };
20 | 7555FF7B242A565900829871 /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = .app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
22 | 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
23 | AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; };
24 | /* End PBXFileReference section */
25 |
26 | /* Begin PBXGroup section */
27 | 058557D7273AAEEB004C7B11 /* Preview Content */ = {
28 | isa = PBXGroup;
29 | children = (
30 | 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
31 | );
32 | path = "Preview Content";
33 | sourceTree = "";
34 | };
35 | 42799AB246E5F90AF97AA0EF /* Frameworks */ = {
36 | isa = PBXGroup;
37 | children = (
38 | );
39 | name = Frameworks;
40 | sourceTree = "";
41 | };
42 | 7555FF72242A565900829871 = {
43 | isa = PBXGroup;
44 | children = (
45 | AB1DB47929225F7C00F7AF9C /* Configuration */,
46 | 7555FF7D242A565900829871 /* iosApp */,
47 | 7555FF7C242A565900829871 /* Products */,
48 | 42799AB246E5F90AF97AA0EF /* Frameworks */,
49 | );
50 | sourceTree = "";
51 | };
52 | 7555FF7C242A565900829871 /* Products */ = {
53 | isa = PBXGroup;
54 | children = (
55 | 7555FF7B242A565900829871 /* .app */,
56 | );
57 | name = Products;
58 | sourceTree = "";
59 | };
60 | 7555FF7D242A565900829871 /* iosApp */ = {
61 | isa = PBXGroup;
62 | children = (
63 | 058557BA273AAA24004C7B11 /* Assets.xcassets */,
64 | 7555FF82242A565900829871 /* ContentView.swift */,
65 | 7555FF8C242A565B00829871 /* Info.plist */,
66 | 2152FB032600AC8F00CF470E /* iOSApp.swift */,
67 | 058557D7273AAEEB004C7B11 /* Preview Content */,
68 | );
69 | path = iosApp;
70 | sourceTree = "";
71 | };
72 | AB1DB47929225F7C00F7AF9C /* Configuration */ = {
73 | isa = PBXGroup;
74 | children = (
75 | AB3632DC29227652001CCB65 /* Config.xcconfig */,
76 | );
77 | path = Configuration;
78 | sourceTree = "";
79 | };
80 | /* End PBXGroup section */
81 |
82 | /* Begin PBXNativeTarget section */
83 | 7555FF7A242A565900829871 /* iosApp */ = {
84 | isa = PBXNativeTarget;
85 | buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
86 | buildPhases = (
87 | F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */,
88 | 7555FF77242A565900829871 /* Sources */,
89 | 7555FF79242A565900829871 /* Resources */,
90 | );
91 | buildRules = (
92 | );
93 | dependencies = (
94 | );
95 | name = iosApp;
96 | productName = iosApp;
97 | productReference = 7555FF7B242A565900829871 /* .app */;
98 | productType = "com.apple.product-type.application";
99 | };
100 | /* End PBXNativeTarget section */
101 |
102 | /* Begin PBXProject section */
103 | 7555FF73242A565900829871 /* Project object */ = {
104 | isa = PBXProject;
105 | attributes = {
106 | LastSwiftUpdateCheck = 1130;
107 | LastUpgradeCheck = 1130;
108 | ORGANIZATIONNAME = orgName;
109 | TargetAttributes = {
110 | 7555FF7A242A565900829871 = {
111 | CreatedOnToolsVersion = 11.3.1;
112 | };
113 | };
114 | };
115 | buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
116 | compatibilityVersion = "Xcode 9.3";
117 | developmentRegion = en;
118 | hasScannedForEncodings = 0;
119 | knownRegions = (
120 | en,
121 | Base,
122 | );
123 | mainGroup = 7555FF72242A565900829871;
124 | productRefGroup = 7555FF7C242A565900829871 /* Products */;
125 | projectDirPath = "";
126 | projectRoot = "";
127 | targets = (
128 | 7555FF7A242A565900829871 /* iosApp */,
129 | );
130 | };
131 | /* End PBXProject section */
132 |
133 | /* Begin PBXResourcesBuildPhase section */
134 | 7555FF79242A565900829871 /* Resources */ = {
135 | isa = PBXResourcesBuildPhase;
136 | buildActionMask = 2147483647;
137 | files = (
138 | 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
139 | 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
140 | );
141 | runOnlyForDeploymentPostprocessing = 0;
142 | };
143 | /* End PBXResourcesBuildPhase section */
144 |
145 | /* Begin PBXShellScriptBuildPhase section */
146 | F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = {
147 | isa = PBXShellScriptBuildPhase;
148 | buildActionMask = 2147483647;
149 | files = (
150 | );
151 | inputFileListPaths = (
152 | );
153 | inputPaths = (
154 | );
155 | name = "Compile Kotlin Framework";
156 | outputFileListPaths = (
157 | );
158 | outputPaths = (
159 | );
160 | runOnlyForDeploymentPostprocessing = 0;
161 | shellPath = /bin/sh;
162 | shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n";
163 | };
164 | /* End PBXShellScriptBuildPhase section */
165 |
166 | /* Begin PBXSourcesBuildPhase section */
167 | 7555FF77242A565900829871 /* Sources */ = {
168 | isa = PBXSourcesBuildPhase;
169 | buildActionMask = 2147483647;
170 | files = (
171 | 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
172 | 7555FF83242A565900829871 /* ContentView.swift in Sources */,
173 | );
174 | runOnlyForDeploymentPostprocessing = 0;
175 | };
176 | /* End PBXSourcesBuildPhase section */
177 |
178 | /* Begin XCBuildConfiguration section */
179 | 7555FFA3242A565B00829871 /* Debug */ = {
180 | isa = XCBuildConfiguration;
181 | baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
182 | buildSettings = {
183 | ALWAYS_SEARCH_USER_PATHS = NO;
184 | CLANG_ANALYZER_NONNULL = YES;
185 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
186 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
187 | CLANG_CXX_LIBRARY = "libc++";
188 | CLANG_ENABLE_MODULES = YES;
189 | CLANG_ENABLE_OBJC_ARC = YES;
190 | CLANG_ENABLE_OBJC_WEAK = YES;
191 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
192 | CLANG_WARN_BOOL_CONVERSION = YES;
193 | CLANG_WARN_COMMA = YES;
194 | CLANG_WARN_CONSTANT_CONVERSION = YES;
195 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
196 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
197 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
198 | CLANG_WARN_EMPTY_BODY = YES;
199 | CLANG_WARN_ENUM_CONVERSION = YES;
200 | CLANG_WARN_INFINITE_RECURSION = YES;
201 | CLANG_WARN_INT_CONVERSION = YES;
202 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
203 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
204 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
205 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
206 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
207 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
208 | CLANG_WARN_STRICT_PROTOTYPES = YES;
209 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
210 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
211 | CLANG_WARN_UNREACHABLE_CODE = YES;
212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
213 | COPY_PHASE_STRIP = NO;
214 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
215 | ENABLE_STRICT_OBJC_MSGSEND = YES;
216 | ENABLE_TESTABILITY = YES;
217 | GCC_C_LANGUAGE_STANDARD = gnu11;
218 | GCC_DYNAMIC_NO_PIC = NO;
219 | GCC_NO_COMMON_BLOCKS = YES;
220 | GCC_OPTIMIZATION_LEVEL = 0;
221 | GCC_PREPROCESSOR_DEFINITIONS = (
222 | "DEBUG=1",
223 | "$(inherited)",
224 | );
225 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
226 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
227 | GCC_WARN_UNDECLARED_SELECTOR = YES;
228 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
229 | GCC_WARN_UNUSED_FUNCTION = YES;
230 | GCC_WARN_UNUSED_VARIABLE = YES;
231 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
232 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
233 | MTL_FAST_MATH = YES;
234 | ONLY_ACTIVE_ARCH = YES;
235 | SDKROOT = iphoneos;
236 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
237 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
238 | };
239 | name = Debug;
240 | };
241 | 7555FFA4242A565B00829871 /* Release */ = {
242 | isa = XCBuildConfiguration;
243 | baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
244 | buildSettings = {
245 | ALWAYS_SEARCH_USER_PATHS = NO;
246 | CLANG_ANALYZER_NONNULL = YES;
247 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
249 | CLANG_CXX_LIBRARY = "libc++";
250 | CLANG_ENABLE_MODULES = YES;
251 | CLANG_ENABLE_OBJC_ARC = YES;
252 | CLANG_ENABLE_OBJC_WEAK = YES;
253 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
254 | CLANG_WARN_BOOL_CONVERSION = YES;
255 | CLANG_WARN_COMMA = YES;
256 | CLANG_WARN_CONSTANT_CONVERSION = YES;
257 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
259 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
260 | CLANG_WARN_EMPTY_BODY = YES;
261 | CLANG_WARN_ENUM_CONVERSION = YES;
262 | CLANG_WARN_INFINITE_RECURSION = YES;
263 | CLANG_WARN_INT_CONVERSION = YES;
264 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
265 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
266 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
268 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
269 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
270 | CLANG_WARN_STRICT_PROTOTYPES = YES;
271 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
272 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
273 | CLANG_WARN_UNREACHABLE_CODE = YES;
274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
275 | COPY_PHASE_STRIP = NO;
276 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
277 | ENABLE_NS_ASSERTIONS = NO;
278 | ENABLE_STRICT_OBJC_MSGSEND = YES;
279 | GCC_C_LANGUAGE_STANDARD = gnu11;
280 | GCC_NO_COMMON_BLOCKS = YES;
281 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
282 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
283 | GCC_WARN_UNDECLARED_SELECTOR = YES;
284 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
285 | GCC_WARN_UNUSED_FUNCTION = YES;
286 | GCC_WARN_UNUSED_VARIABLE = YES;
287 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
288 | MTL_ENABLE_DEBUG_INFO = NO;
289 | MTL_FAST_MATH = YES;
290 | SDKROOT = iphoneos;
291 | SWIFT_COMPILATION_MODE = wholemodule;
292 | SWIFT_OPTIMIZATION_LEVEL = "-O";
293 | VALIDATE_PRODUCT = YES;
294 | };
295 | name = Release;
296 | };
297 | 7555FFA6242A565B00829871 /* Debug */ = {
298 | isa = XCBuildConfiguration;
299 | buildSettings = {
300 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
301 | CODE_SIGN_IDENTITY = "Apple Development";
302 | CODE_SIGN_STYLE = Automatic;
303 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
304 | DEVELOPMENT_TEAM = "${TEAM_ID}";
305 | ENABLE_PREVIEWS = YES;
306 | FRAMEWORK_SEARCH_PATHS = (
307 | "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
308 | );
309 | INFOPLIST_FILE = iosApp/Info.plist;
310 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
311 | LD_RUNPATH_SEARCH_PATHS = (
312 | "$(inherited)",
313 | "@executable_path/Frameworks",
314 | );
315 | OTHER_LDFLAGS = (
316 | "$(inherited)",
317 | "-framework",
318 | composeApp,
319 | );
320 | PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
321 | PRODUCT_NAME = "${APP_NAME}";
322 | PROVISIONING_PROFILE_SPECIFIER = "";
323 | SWIFT_VERSION = 5.0;
324 | TARGETED_DEVICE_FAMILY = "1,2";
325 | };
326 | name = Debug;
327 | };
328 | 7555FFA7242A565B00829871 /* Release */ = {
329 | isa = XCBuildConfiguration;
330 | buildSettings = {
331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
332 | CODE_SIGN_IDENTITY = "Apple Development";
333 | CODE_SIGN_STYLE = Automatic;
334 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
335 | DEVELOPMENT_TEAM = "${TEAM_ID}";
336 | ENABLE_PREVIEWS = YES;
337 | FRAMEWORK_SEARCH_PATHS = (
338 | "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
339 | );
340 | INFOPLIST_FILE = iosApp/Info.plist;
341 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
342 | LD_RUNPATH_SEARCH_PATHS = (
343 | "$(inherited)",
344 | "@executable_path/Frameworks",
345 | );
346 | OTHER_LDFLAGS = (
347 | "$(inherited)",
348 | "-framework",
349 | composeApp,
350 | );
351 | PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
352 | PRODUCT_NAME = "${APP_NAME}";
353 | PROVISIONING_PROFILE_SPECIFIER = "";
354 | SWIFT_VERSION = 5.0;
355 | TARGETED_DEVICE_FAMILY = "1,2";
356 | };
357 | name = Release;
358 | };
359 | /* End XCBuildConfiguration section */
360 |
361 | /* Begin XCConfigurationList section */
362 | 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
363 | isa = XCConfigurationList;
364 | buildConfigurations = (
365 | 7555FFA3242A565B00829871 /* Debug */,
366 | 7555FFA4242A565B00829871 /* Release */,
367 | );
368 | defaultConfigurationIsVisible = 0;
369 | defaultConfigurationName = Release;
370 | };
371 | 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
372 | isa = XCConfigurationList;
373 | buildConfigurations = (
374 | 7555FFA6242A565B00829871 /* Debug */,
375 | 7555FFA7242A565B00829871 /* Release */,
376 | );
377 | defaultConfigurationIsVisible = 0;
378 | defaultConfigurationName = Release;
379 | };
380 | /* End XCConfigurationList section */
381 | };
382 | rootObject = 7555FF73242A565900829871 /* Project object */;
383 | }
384 |
--------------------------------------------------------------------------------
/composeApp/src/wasmJsMain/kotlin/TreeSitterInterop.kt:
--------------------------------------------------------------------------------
1 | import kotlin.js.Promise
2 |
3 | /**
4 | * Represents a position in a text document, expressed as a zero-based line and column
5 | */
6 | external interface Point : JsAny {
7 | /** The zero-based line number */
8 | val row: Int
9 |
10 | /** The zero-based character offset on the line */
11 | val column: Int
12 | }
13 |
14 | /**
15 | * Represents a range in a text document, expressed by its start and end positions
16 | */
17 | external interface Range : JsAny {
18 | /** The start byte index of the range */
19 | val startIndex: Int
20 |
21 | /** The end byte index of the range */
22 | val endIndex: Int
23 |
24 | /** The start position of the range */
25 | val startPosition: Point
26 |
27 | /** The end position of the range */
28 | val endPosition: Point
29 | }
30 |
31 | /**
32 | * Represents an edit operation that can be applied to a syntax tree
33 | */
34 | external interface Edit : JsAny {
35 | /** The start byte index of the edit */
36 | val startIndex: Int
37 |
38 | /** The previous end byte index of the edit */
39 | val oldEndIndex: Int
40 |
41 | /** The new end byte index of the edit */
42 | val newEndIndex: Int
43 |
44 | /** The start position of the edit */
45 | val startPosition: Point
46 |
47 | /** The previous end position of the edit */
48 | val oldEndPosition: Point
49 |
50 | /** The new end position of the edit */
51 | val newEndPosition: Point
52 | }
53 |
54 | // Logger
55 | typealias Logger = (message: String, params: JsAny, type: String) -> Unit
56 |
57 | // Input
58 | typealias Input = (index: Int, position: Point?) -> String?
59 |
60 | /**
61 | * Options for parsing
62 | *
63 | * The `includedRanges` property is an array of {@link Range} objects that
64 | * represent the ranges of text that the parser should include when parsing.
65 | *
66 | * See {@link Parser#parse} for more information.
67 | */
68 | external interface Options : JsAny {
69 | /**
70 | * An array of {@link Range} objects that
71 | * represent the ranges of text that the parser should include when parsing.
72 | *
73 | * This sets the ranges of text that the parser should include when parsing.
74 | * By default, the parser will always include entire documents. This
75 | * function allows you to parse only a *portion* of a document but
76 | * still return a syntax tree whose ranges match up with the document
77 | * as a whole. You can also pass multiple disjoint ranges.
78 | * If `ranges` is empty, then the entire document will be parsed.
79 | * Otherwise, the given ranges must be ordered from earliest to latest
80 | * in the document, and they must not overlap.
81 | */
82 | var includedRanges: JsArray?
83 | }
84 |
85 | /**
86 | * Options for query execution
87 | */
88 | external interface QueryOptions : JsAny {
89 | /** The start position of the range to query */
90 | var startPosition: Point?
91 |
92 | /** The end position of the range to query */
93 | var endPosition: Point?
94 |
95 | /** The start index of the range to query */
96 | var startIndex: Int?
97 |
98 | /** The end index of the range to query */
99 | var endIndex: Int?
100 |
101 | /**
102 | * The maximum number of in-progress matches for this query.
103 | * The limit must be > 0 and <= 65536.
104 | */
105 | var matchLimit: Int?
106 |
107 | /**
108 | * The maximum start depth for a query cursor.
109 | *
110 | * This prevents cursors from exploring children nodes at a certain depth.
111 | * Note if a pattern includes many children, then they will still be
112 | * checked.
113 | *
114 | * The zero max start depth value can be used as a special behavior and
115 | * it helps to destructure a subtree by staying on a node and using
116 | * captures for interested parts. Note that the zero max start depth
117 | * only limit a search depth for a pattern's root node but other nodes
118 | * that are parts of the pattern may be searched at any depth what
119 | * defined by the pattern structure.
120 | *
121 | * Set to `null` to remove the maximum start depth.
122 | */
123 | var maxStartDepth: Int?
124 | }
125 |
126 | /**
127 | * A particular {@link Node} that has been captured with a particular name within a
128 | * {@link Query}.
129 | */
130 | external interface QueryCapture : JsAny {
131 | /** The name of the capture */
132 | val name: String
133 |
134 | val text: String?
135 |
136 | /** The captured node */
137 | val node: SyntaxNode
138 |
139 | /** The properties for predicates declared with the operator `set!`. */
140 | val setProperties: JsAny?
141 |
142 | /** The properties for predicates declared with the operator `is?`. */
143 | val assertedProperties: JsAny?
144 |
145 | /** The properties for predicates declared with the operator `is-not?`. */
146 | val refutedProperties: JsAny?
147 | }
148 |
149 | /** A match of a {@link Query} to a particular set of {@link Node}s. */
150 | external interface QueryMatch : JsAny {
151 | /** The index of the pattern that matched. */
152 | val pattern: Int
153 |
154 | /** The captures associated with the match. */
155 | val captures: JsArray
156 | }
157 |
158 | /**
159 | * A predicate that contains an operator and list of operands.
160 | */
161 | external interface PredicateResult : JsAny {
162 | /** The operator of the predicate, like `match?`, `eq?`, `set!`, etc. */
163 | val operator: String
164 |
165 | /** The operands of the predicate, which are either captures or strings. */
166 | val operands: JsArray
167 | }
168 |
169 | external interface Query : JsAny {
170 | /** The names of the captures used in the query. */
171 | val captureNames: JsArray
172 |
173 | /**
174 | * The other user-defined predicates associated with the given index.
175 | *
176 | * This includes predicates with operators other than:
177 | * - `match?`
178 | * - `eq?` and `not-eq?`
179 | * - `any-of?` and `not-any-of?`
180 | * - `is?` and `is-not?`
181 | * - `set!`
182 | */
183 | val predicates: JsArray
184 |
185 | /** The properties for predicates with the operator `set!`. */
186 | val setProperties: JsArray
187 |
188 | /** The properties for predicates with the operator `is?`. */
189 | val assertedProperties: JsArray
190 |
191 | /** The properties for predicates with the operator `is-not?`. */
192 | val refutedProperties: JsArray
193 |
194 | /** The maximum number of in-progress matches for this cursor. */
195 | val matchLimit: Int
196 |
197 | /** Delete the query, freeing its resources. */
198 | fun delete()
199 |
200 | /// 如果一个方法是 optional,在 Kotlin 不要传 null 过来,简单一点,再复制一个不带 null 的
201 |
202 | /**
203 | * Iterate over all of the individual captures in the order that they
204 | * appear.
205 | *
206 | * This is useful if you don't care about which pattern matched, and just
207 | * want a single, ordered sequence of captures.
208 | *
209 | * @param node - The node to execute the query on.
210 | *
211 | * @param options - Options for query execution.
212 | */
213 | fun captures(node: SyntaxNode, options: QueryOptions?): JsArray
214 |
215 | /**
216 | * Iterate over all of the individual captures in the order that they
217 | * appear.
218 | *
219 | * This is useful if you don't care about which pattern matched, and just
220 | * want a single, ordered sequence of captures.
221 | *
222 | * @param node - The node to execute the query on.
223 | */
224 | fun captures(node: SyntaxNode): JsArray
225 |
226 | /**
227 | * Iterate over all of the matches in the order that they were found.
228 | *
229 | * Each match contains the index of the pattern that matched, and a list of
230 | * captures. Because multiple patterns can match the same set of nodes,
231 | * one match may contain captures that appear *before* some of the
232 | * captures from a previous match.
233 | *
234 | * @param node - The node to execute the query on.
235 | */
236 | fun matches(node: SyntaxNode): JsArray
237 |
238 | /**
239 | * Iterate over all of the matches in the order that they were found.
240 | *
241 | * Each match contains the index of the pattern that matched, and a list of
242 | * captures. Because multiple patterns can match the same set of nodes,
243 | * one match may contain captures that appear *before* some of the
244 | * captures from a previous match.
245 | *
246 | * @param node - The node to execute the query on.
247 | *
248 | * @param options - Options for query execution.
249 | */
250 | fun matches(node: SyntaxNode, options: QueryOptions?): JsArray
251 |
252 | /** Get the predicates for a given pattern. */
253 | fun predicatesForPattern(patternIndex: Int): JsArray
254 |
255 | /**
256 | * Disable a certain capture within a query.
257 | *
258 | * This prevents the capture from being returned in matches, and also
259 | * avoids any resource usage associated with recording the capture.
260 | */
261 | fun disableCapture(captureName: String)
262 |
263 | /**
264 | * Disable a certain pattern within a query.
265 | *
266 | * This prevents the pattern from matching, and also avoids any resource
267 | * usage associated with the pattern. This throws an error if the pattern
268 | * index is out of bounds.
269 | */
270 | fun disablePattern(patternIndex: Int)
271 |
272 | /**
273 | * Check if a given step in a query is 'definite'.
274 | *
275 | * A query step is 'definite' if its parent pattern will be guaranteed to
276 | * match successfully once it reaches the step.
277 | */
278 | fun isPatternGuaranteedAtStep(byteOffset: Int): Boolean
279 |
280 | /** Check if a given pattern within a query has a single root node. */
281 | fun isPatternRooted(patternIndex: Int): Boolean
282 |
283 | /** Check if a given pattern within a query has a single root node. */
284 | fun isPatternNonLocal(patternIndex: Int): Boolean
285 |
286 | /** Get the byte offset where the given pattern starts in the query's source. */
287 | fun startIndexForPattern(patternIndex: Int): Int
288 |
289 | /**
290 | * Check if, on its last execution, this cursor exceeded its maximum number
291 | * of in-progress matches.
292 | */
293 | fun didExceedMatchLimit(): Boolean
294 | }
295 |
296 | /** A single node within a syntax {@link Tree}. */
297 | external interface SyntaxNode : JsAny {
298 | /** The tree that this node belongs to. */
299 | val tree: Tree
300 |
301 | /**
302 | * The numeric id for this node that is unique.
303 | *
304 | * Within a given syntax tree, no two nodes have the same id. However:
305 | *
306 | * * If a new tree is created based on an older tree, and a node from the old tree is reused in
307 | * the process, then that node will have the same id in both trees.
308 | *
309 | * * A node not marked as having changes does not guarantee it was reused.
310 | *
311 | * * If a node is marked as having changed in the old tree, it will not be reused.
312 | */
313 | val id: Int
314 |
315 | /** Get this node's type as a numerical id. */
316 | val typeId: Int
317 |
318 | /**
319 | * Get the node's type as a numerical id as it appears in the grammar,
320 | * ignoring aliases.
321 | */
322 | val grammarId: Int
323 |
324 | /** Get this node's type as a string. */
325 | val type: String
326 |
327 | /**
328 | * Get this node's symbol name as it appears in the grammar, ignoring
329 | * aliases as a string.
330 | */
331 | val grammarType: String
332 |
333 | /**
334 | * Check if this node is *named*.
335 | *
336 | * Named nodes correspond to named rules in the grammar, whereas
337 | * *anonymous* nodes correspond to string literals in the grammar.
338 | */
339 | val isNamed: Boolean
340 |
341 | /**
342 | * Check if this node is *missing*.
343 | *
344 | * Missing nodes are inserted by the parser in order to recover from
345 | * certain kinds of syntax errors.
346 | */
347 | val isMissing: Boolean
348 |
349 | /**
350 | * Check if this node is *extra*.
351 | *
352 | * Extra nodes represent things like comments, which are not required
353 | * by the grammar, but can appear anywhere.
354 | */
355 | val isExtra: Boolean
356 |
357 | /** Check if this node has been edited. */
358 | val hasChanges: Boolean
359 |
360 | /**
361 | * Check if this node represents a syntax error or contains any syntax
362 | * errors anywhere within it.
363 | */
364 | val hasError: Boolean
365 |
366 | /**
367 | * Check if this node represents a syntax error.
368 | *
369 | * Syntax errors represent parts of the code that could not be incorporated
370 | * into a valid syntax tree.
371 | */
372 | val isError: Boolean
373 |
374 | /** Get the string content of this node. */
375 | val text: String
376 |
377 | /** Get this node's parse state. */
378 | val parseState: Int
379 |
380 | /** Get the parse state after this node. */
381 | val nextParseState: Int
382 |
383 | /** The position where this node starts. */
384 | val startPosition: Point
385 |
386 | /** The position where this node ends. */
387 | val endPosition: Point
388 |
389 | /** The byte index where this node starts. */
390 | val startIndex: Int
391 |
392 | /** The byte index where this node ends. */
393 | val endIndex: Int
394 |
395 | /**
396 | * Get this node's immediate parent.
397 | * Prefer {@link Node#childWithDescendant} for iterating over this node's ancestors.
398 | */
399 | val parent: SyntaxNode?
400 |
401 | /**
402 | * Iterate over this node's children.
403 | *
404 | * If you're walking the tree recursively, you may want to use the
405 | * {@link TreeCursor} APIs directly instead.
406 | */
407 | val children: JsArray
408 |
409 | /**
410 | * Iterate over this node's named children.
411 | *
412 | * See also {@link Node#children}.
413 | */
414 | val namedChildren: JsArray
415 |
416 | /** Get this node's number of children. */
417 | val childCount: Int
418 |
419 | /**
420 | * Get this node's number of *named* children.
421 | *
422 | * See also {@link Node#isNamed}.
423 | */
424 | val namedChildCount: Int
425 |
426 | /** Get this node's first child. */
427 | val firstChild: SyntaxNode?
428 |
429 | /**
430 | * Get this node's first named child.
431 | *
432 | * See also {@link Node#isNamed}.
433 | */
434 | val firstNamedChild: SyntaxNode?
435 |
436 | /** Get this node's last child. */
437 | val lastChild: SyntaxNode?
438 |
439 | /**
440 | * Get this node's last named child.
441 | *
442 | * See also {@link Node#isNamed}.
443 | */
444 | val lastNamedChild: SyntaxNode?
445 |
446 | /** Get this node's next sibling. */
447 | val nextSibling: SyntaxNode?
448 |
449 | /**
450 | * Get this node's next *named* sibling.
451 | *
452 | * See also {@link Node#isNamed}.
453 | */
454 | val nextNamedSibling: SyntaxNode?
455 |
456 | /** Get this node's previous sibling. */
457 | val previousSibling: SyntaxNode?
458 |
459 | /**
460 | * Get this node's previous *named* sibling.
461 | *
462 | * See also {@link Node#isNamed}.
463 | */
464 | val previousNamedSibling: SyntaxNode?
465 |
466 | /** Get the node's number of descendants, including one for the node itself. */
467 | val descendantCount: Int
468 |
469 | /** Check if this node is equal to another node. */
470 | fun equals(other: SyntaxNode): Boolean
471 |
472 | /**
473 | * Get the node's child at the given index, where zero represents the first child.
474 | *
475 | * This method is fairly fast, but its cost is technically log(n), so if
476 | * you might be iterating over a long list of children, you should use
477 | * {@link Node#children} instead.
478 | */
479 | fun child(index: Int): SyntaxNode?
480 |
481 | /**
482 | * Get this node's *named* child at the given index.
483 | *
484 | * See also {@link Node#isNamed}.
485 | * This method is fairly fast, but its cost is technically log(n), so if
486 | * you might be iterating over a long list of children, you should use
487 | * {@link Node#namedChildren} instead.
488 | */
489 | fun namedChild(index: Int): SyntaxNode?
490 |
491 | /**
492 | * Get the first child with the given field name.
493 | *
494 | * If multiple children may have the same field name, access them using
495 | * {@link Node#childrenForFieldName}.
496 | */
497 | fun childForFieldName(fieldName: String): SyntaxNode?
498 |
499 | /**
500 | * Get this node's child with the given numerical field id.
501 | *
502 | * See also {@link Node#childForFieldName}. You can
503 | * convert a field name to an id using {@link Language#fieldIdForName}.
504 | */
505 | fun childForFieldId(fieldId: Int): SyntaxNode?
506 |
507 | /** Get the field name of this node's child at the given index. */
508 | fun fieldNameForChild(childIndex: Int): String?
509 |
510 | /**
511 | * Get an array of this node's children with a given field name.
512 | *
513 | * See also {@link Node#children}.
514 | */
515 | fun childrenForFieldName(fieldName: String): JsArray
516 |
517 | /**
518 | * Get an array of this node's children with a given field id.
519 | *
520 | * See also {@link Node#childrenForFieldName}.
521 | */
522 | fun childrenForFieldId(fieldId: Int): JsArray
523 |
524 | /** Get the node's first child that contains or starts after the given byte offset. */
525 | fun firstChildForIndex(index: Int): SyntaxNode?
526 |
527 | /** Get the node's first named child that contains or starts after the given byte offset. */
528 | fun firstNamedChildForIndex(index: Int): SyntaxNode?
529 |
530 | /** Get the smallest node within this node that spans the given byte range. */
531 | fun descendantForIndex(index: Int): SyntaxNode
532 |
533 | /** Get the smallest node within this node that spans the given byte range. */
534 | fun descendantForIndex(startIndex: Int, endIndex: Int): SyntaxNode
535 |
536 | /** Get the smallest named node within this node that spans the given byte range. */
537 | fun namedDescendantForIndex(index: Int): SyntaxNode
538 |
539 | /** Get the smallest named node within this node that spans the given byte range. */
540 | fun namedDescendantForIndex(startIndex: Int, endIndex: Int): SyntaxNode
541 |
542 | /** Get the smallest node within this node that spans the given point range. */
543 | fun descendantForPosition(position: Point): SyntaxNode
544 |
545 | /** Get the smallest node within this node that spans the given point range. */
546 | fun descendantForPosition(startPosition: Point, endPosition: Point): SyntaxNode
547 |
548 | /** Get the smallest named node within this node that spans the given point range. */
549 | fun namedDescendantForPosition(position: Point): SyntaxNode
550 |
551 | /** Get the smallest named node within this node that spans the given point range. */
552 | fun namedDescendantForPosition(startPosition: Point, endPosition: Point): SyntaxNode
553 |
554 | /**
555 | * Get the descendants of this node that are the given type, or in the given types array.
556 | *
557 | * The types array should contain node type strings, which can be retrieved from {@link Language#types}.
558 | *
559 | * Additionally, a `startPosition` and `endPosition` can be passed in to restrict the search to a byte range.
560 | */
561 | fun descendantsOfType(types: JsAny, startPosition: Point?, endPosition: Point?): JsArray
562 |
563 | /**
564 | * Create a new {@link TreeCursor} starting from this node.
565 | *
566 | * Note that the given node is considered the root of the cursor,
567 | * and the cursor cannot walk outside this node.
568 | */
569 | fun walk(): TreeCursor
570 | }
571 |
572 | /** A stateful object for walking a syntax {@link Tree} efficiently. */
573 | // TreeCursor
574 | external interface TreeCursor : JsAny {
575 | /** Get the type of the cursor's current node. */
576 | val nodeType: String
577 |
578 | /** Get the type id of the cursor's current node. */
579 | val nodeTypeId: Int
580 |
581 | /** Get the state id of the cursor's current node. */
582 | val nodeStateId: Int
583 |
584 | /** Get the string content of the cursor's current node. */
585 | val nodeText: String
586 |
587 | /** Get the id of the cursor's current node. */
588 | val nodeId: Int
589 |
590 | /**
591 | * Check if the cursor's current node is *named*.
592 | *
593 | * Named nodes correspond to named rules in the grammar, whereas
594 | * *anonymous* nodes correspond to string literals in the grammar.
595 | */
596 | val nodeIsNamed: Boolean
597 |
598 | /**
599 | * Check if the cursor's current node is *missing*.
600 | *
601 | * Missing nodes are inserted by the parser in order to recover from
602 | * certain kinds of syntax errors.
603 | */
604 | val nodeIsMissing: Boolean
605 |
606 | /** Get the start position of the cursor's current node. */
607 | val startPosition: Point
608 |
609 | /** Get the end position of the cursor's current node. */
610 | val endPosition: Point
611 |
612 | /** Get the start index of the cursor's current node. */
613 | val startIndex: Int
614 |
615 | /** Get the end index of the cursor's current node. */
616 | val endIndex: Int
617 |
618 | /** Get the tree cursor's current {@link Node}. */
619 | val currentNode: SyntaxNode
620 |
621 | /** Get the field name of this tree cursor's current node. */
622 | val currentFieldName: String
623 |
624 | /**
625 | * Get the numerical field id of this tree cursor's current node.
626 | *
627 | * See also {@link TreeCursor#currentFieldName}.
628 | */
629 | val currentFieldId: Int
630 |
631 | /**
632 | * Get the depth of the cursor's current node relative to the original
633 | * node that the cursor was constructed with.
634 | */
635 | val currentDepth: Int
636 |
637 | /**
638 | * Get the index of the cursor's current node out of all of the
639 | * descendants of the original node that the cursor was constructed with.
640 | */
641 | val currentDescendantIndex: Int
642 |
643 | /**
644 | * Re-initialize this tree cursor to start at the original node that the
645 | * cursor was constructed with.
646 | */
647 | fun reset(node: SyntaxNode)
648 |
649 | /**
650 | * Re-initialize a tree cursor to the same position as another cursor.
651 | *
652 | * Unlike {@link TreeCursor#reset}, this will not lose parent
653 | * information and allows reusing already created cursors.
654 | */
655 | fun resetTo(cursor: TreeCursor)
656 |
657 | /** Delete the tree cursor, freeing its resources. */
658 | fun delete()
659 |
660 | /**
661 | * Move this cursor to the parent of its current node.
662 | *
663 | * This returns `true` if the cursor successfully moved, and returns
664 | * `false` if there was no parent node (the cursor was already on the
665 | * root node).
666 | *
667 | * Note that the node the cursor was constructed with is considered the root
668 | * of the cursor, and the cursor cannot walk outside this node.
669 | */
670 | fun gotoParent(): Boolean
671 |
672 | /**
673 | * Move this cursor to the first child of its current node.
674 | *
675 | * This returns `true` if the cursor successfully moved, and returns
676 | * `false` if there were no children.
677 | */
678 | fun gotoFirstChild(): Boolean
679 |
680 | /**
681 | * Move this cursor to the last child of its current node.
682 | *
683 | * This returns `true` if the cursor successfully moved, and returns
684 | * `false` if there were no children.
685 | *
686 | * Note that this function may be slower than
687 | * {@link TreeCursor#gotoFirstChild} because it needs to
688 | * iterate through all the children to compute the child's position.
689 | */
690 | fun gotoLastChild(): Boolean
691 |
692 | /**
693 | * Move this cursor to the first child of its current node that contains or
694 | * starts after the given byte offset.
695 | *
696 | * This returns `true` if the cursor successfully moved to a child node, and returns
697 | * `false` if no such child was found.
698 | */
699 | fun gotoFirstChildForIndex(goalIndex: Int): Boolean
700 |
701 | /**
702 | * Move this cursor to the first child of its current node that contains or
703 | * starts after the given byte offset.
704 | *
705 | * This returns the index of the child node if one was found, and returns
706 | * `null` if no such child was found.
707 | */
708 | fun gotoFirstChildForPosition(goalPosition: Point): Boolean
709 |
710 | /**
711 | * Move this cursor to the next sibling of its current node.
712 | *
713 | * This returns `true` if the cursor successfully moved, and returns
714 | * `false` if there was no next sibling node.
715 | *
716 | * Note that the node the cursor was constructed with is considered the root
717 | * of the cursor, and the cursor cannot walk outside this node.
718 | */
719 | fun gotoNextSibling(): Boolean
720 |
721 | /**
722 | * Move this cursor to the previous sibling of its current node.
723 | *
724 | * This returns `true` if the cursor successfully moved, and returns
725 | * `false` if there was no previous sibling node.
726 | *
727 | * Note that this function may be slower than
728 | * {@link TreeCursor#gotoNextSibling} due to how node
729 | * positions are stored. In the worst case, this will need to iterate
730 | * through all the children up to the previous sibling node to recalculate
731 | * its position. Also note that the node the cursor was constructed with is
732 | * considered the root of the cursor, and the cursor cannot walk outside this node.
733 | */
734 | fun gotoPreviousSibling(): Boolean
735 |
736 | /**
737 | * Move the cursor to the node that is the nth descendant of
738 | * the original node that the cursor was constructed with, where
739 | * zero represents the original node itself.
740 | */
741 | fun gotoDescendant(goalDescendantIndex: Int)
742 | }
743 |
744 | /** A tree that represents the syntactic structure of a source code file. */
745 | // Tree
746 | external interface Tree : JsAny {
747 | /** Get the root node of the syntax tree. */
748 | val rootNode: SyntaxNode
749 |
750 | /**
751 | * Get the root node of the syntax tree, but with its position shifted
752 | * forward by the given offset.
753 | */
754 | fun rootNodeWithOffset(offsetBytes: Int, offsetExtent: Point): SyntaxNode
755 |
756 | /** Create a shallow copy of the syntax tree. This is very fast. */
757 | fun copy(): Tree
758 |
759 | /** Delete the syntax tree, freeing its resources. */
760 | fun delete()
761 |
762 | /**
763 | * Edit the syntax tree to keep it in sync with source code that has been
764 | * edited.
765 | *
766 | * You must describe the edit both in terms of byte offsets and in terms of
767 | * row/column coordinates.
768 | */
769 | fun edit(edit: Edit): Tree
770 |
771 | /** Create a new {@link TreeCursor} starting from the root of the tree. */
772 | fun walk(): TreeCursor
773 |
774 | /**
775 | * Compare this old edited syntax tree to a new syntax tree representing
776 | * the same document, returning a sequence of ranges whose syntactic
777 | * structure has changed.
778 | *
779 | * For this to work correctly, this syntax tree must have been edited such
780 | * that its ranges match up to the new tree. Generally, you'll want to
781 | * call this method right after calling one of the [`Parser::parse`]
782 | * functions. Call it on the old tree that was passed to parse, and
783 | * pass the new tree that was returned from `parse`.
784 | */
785 | fun getChangedRanges(other: Tree): JsArray
786 |
787 | /** Get the included ranges that were used to parse the syntax tree. */
788 | fun getIncludedRanges(): JsArray
789 |
790 | fun getEditedRange(other: Tree): Range
791 | }
792 |
793 | // LookaheadIterable
794 | external interface LookaheadIterable : JsAny {
795 | /** Get the current symbol of the lookahead iterator. */
796 | val currentTypeId: Int
797 |
798 | /** Get the current symbol name of the lookahead iterator. */
799 | val currentType: String
800 |
801 | /** Delete the lookahead iterator, freeing its resources. */
802 | fun delete()
803 |
804 | /**
805 | * Reset the lookahead iterator to another state.
806 | *
807 | * This returns `true` if the iterator was reset to the given state and
808 | * `false` otherwise.
809 | */
810 | fun resetState(stateId: Int): Boolean
811 | }
812 |
813 | /**
814 | * A stateful object that is used to produce a {@link Tree} based on some
815 | * source code.
816 | */
817 | // Parser - 保持与原 WebTreeSitter 结构兼容
818 | @JsModule("web-tree-sitter")
819 | external object WebTreeSitter {
820 | @JsName("default")
821 | class Parser : JsAny {
822 | companion object Companion {
823 | /**
824 | * This must always be called before creating a Parser.
825 | *
826 | * You can optionally pass in options to configure the Wasm module, the most common
827 | * one being `locateFile` to help the module find the `.wasm` file.
828 | */
829 | fun init(): Promise
830 | }
831 |
832 | /**
833 | * Delete the parser, freeing its resources.
834 | */
835 | fun delete()
836 |
837 | /**
838 | * Parse a slice of UTF8 text.
839 | */
840 | fun parse(input: String): Tree
841 |
842 | /**
843 | * Get the ranges of text that the parser will include when parsing.
844 | */
845 | fun getIncludedRanges(): JsArray
846 |
847 | fun getTimeoutMicros(): Int
848 |
849 | fun setTimeoutMicros(timeout: Int)
850 |
851 | /**
852 | * Instruct the parser to start the next parse from the beginning.
853 | *
854 | * If the parser previously failed because of a callback,
855 | * then by default, it will resume where it left off on the
856 | * next call to {@link Parser#parse} or other parsing functions.
857 | * If you don't want to resume, and instead intend to use this parser to
858 | * parse some other document, you must call `reset` first.
859 | */
860 | fun reset()
861 |
862 | /**
863 | * Set the language that the parser should use for parsing.
864 | *
865 | * If the language was not successfully assigned, an error will be thrown.
866 | * This happens if the language was generated with an incompatible
867 | * version of the Tree-sitter CLI. Check the language's version using
868 | * {@link Language#version} and compare it to this library's
869 | * {@link LANGUAGE_VERSION} and {@link MIN_COMPATIBLE_VERSION} constants.
870 | */
871 | fun setLanguage(language: Language?)
872 |
873 | /**
874 | * An opaque object that defines how to parse a particular language.
875 | * The code for each `Language` is generated by the Tree-sitter CLI.
876 | */
877 | class Language : JsAny {
878 | /**
879 | * Gets the ABI version of the language.
880 | */
881 | val version: Int
882 |
883 | /**
884 | * Gets the number of fields in the language.
885 | */
886 | val fieldCount: Int
887 |
888 | /**
889 | * Gets the number of states in the language.
890 | */
891 | val stateCount: Int
892 |
893 | /**
894 | * Gets the number of node types in the language.
895 | */
896 | val nodeTypeCount: Int
897 |
898 | /**
899 | * Get the field name for a field id.
900 | */
901 | fun fieldNameForId(fieldId: Int): String?
902 |
903 | /**
904 | * Get the field id for a field name.
905 | */
906 | fun fieldIdForName(fieldName: String): Int?
907 |
908 | /**
909 | * Get the node type id for a node type name.
910 | */
911 | fun idForNodeType(type: String, named: Boolean): Int
912 |
913 | /**
914 | * Get the node type name for a node type id.
915 | */
916 | fun nodeTypeForId(typeId: Int): String?
917 |
918 | /**
919 | * Check if a node type is named.
920 | *
921 | * @see {@link https://tree-sitter.github.io/tree-sitter/using-parsers/2-basic-parsing.html#named-vs-anonymous-nodes}
922 | */
923 | fun nodeTypeIsNamed(typeId: Int): Boolean
924 |
925 | /**
926 | * Check if a node type is visible.
927 | */
928 | fun nodeTypeIsVisible(typeId: Int): Boolean
929 |
930 | /**
931 | * Get the next state id for a given state id and node type id.
932 | */
933 | fun nextState(stateId: Int, typeId: Int): Int
934 |
935 | /**
936 | * Create a new query from a string containing one or more S-expression
937 | * patterns.
938 | *
939 | * The query is associated with a particular language, and can only be run
940 | * on syntax nodes parsed with that language. References to Queries can be
941 | * shared between multiple threads.
942 | *
943 | * @link {@see https://tree-sitter.github.io/tree-sitter/using-parsers/queries}
944 | */
945 | fun query(source: String): Query
946 |
947 | /**
948 | * Create a new lookahead iterator for this language and parse state.
949 | *
950 | * This returns `null` if state is invalid for this language.
951 | *
952 | * Iterating {@link LookaheadIterator} will yield valid symbols in the given
953 | * parse state. Newly created lookahead iterators will return the `ERROR`
954 | * symbol from {@link LookaheadIterator#currentType}.
955 | *
956 | * Lookahead iterators can be useful for generating suggestions and improving
957 | * syntax error diagnostics. To get symbols valid in an `ERROR` node, use the
958 | * lookahead iterator on its first leaf node state. For `MISSING` nodes, a
959 | * lookahead iterator created on the previous non-extra leaf node may be
960 | * appropriate.
961 | */
962 | fun lookaheadIterator(stateId: Int): LookaheadIterable?
963 |
964 | companion object Companion {
965 | /**
966 | * Load a language from a WebAssembly module.
967 | * The module can be provided as a path to a file or as a buffer.
968 | */
969 | fun load(input: String): Promise
970 | }
971 | }
972 | }
973 | }
974 |
975 | typealias TreeSitterParser = WebTreeSitter.Parser
976 | typealias TreeSitterTree = Tree
977 | typealias TreeSitterNode = SyntaxNode
978 | typealias TreeSitterPoint = Point
979 |
--------------------------------------------------------------------------------