Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1b069cb
fix: add ability to re-write class name references in desugar plugin
itsaky-adfa Mar 19, 2026
a70fd66
feat: integrate Kotlin analysis API
itsaky-adfa Mar 19, 2026
4ca1e97
Merge branch 'stage' into fix/ADFA-3366-include-analysis-api-as-depen…
itsaky-adfa Mar 19, 2026
43286a6
fix: update kotlin-android to latest version
itsaky-adfa Mar 23, 2026
f1fe62f
fix: remove UnsafeImpl
itsaky-adfa Mar 25, 2026
6e6d8b3
fix: update kotlin-android to latest version
itsaky-adfa Mar 25, 2026
b208658
fix: replace usages of Unsafe with UnsafeImpl
itsaky-adfa Mar 24, 2026
c844ad2
fix: make Kotlin LSP no-op
itsaky-adfa Mar 23, 2026
58db2cb
feat: configure K2 standalone session when setting up LSP
itsaky-adfa Mar 24, 2026
bf7acd1
fix: JvmTarget resolution fails for input "21"
itsaky-adfa Mar 24, 2026
dc62a51
fix: do not early-init VirtualFileSystem
itsaky-adfa Mar 24, 2026
4b1c8e4
fix: remove replaceClass desugar instruction for Unsafe
itsaky-adfa Mar 25, 2026
b14f6ee
fix: ensure boot class path is added as dependency to Android modules
itsaky-adfa Mar 26, 2026
54ca7a9
feat: add diagnostic provider for Kotlin
itsaky-adfa Mar 26, 2026
6e4d458
fix: remove unnecessary log statement
itsaky-adfa Mar 26, 2026
5d841a3
fix: update to latest kotlin-android release
itsaky-adfa Mar 26, 2026
4b7b0f2
fix: always re-initialize K2 session on setupWithProject
itsaky-adfa Mar 26, 2026
e2f137a
fix: diagnostics are always collected from the on-disk file
itsaky-adfa Mar 26, 2026
09fc9ea
feat: add the ability to incrementally invalidate source roots on pro…
itsaky-adfa Mar 30, 2026
dd3d519
fix: dispatch build-related events from GradleBuildService
itsaky-adfa Mar 31, 2026
d6defa6
feat: introduct KtFileManager
itsaky-adfa Mar 31, 2026
4666893
fix: add initial K2-backed scope code completions
itsaky-adfa Apr 1, 2026
2f982a7
feat: add member completions backed by K2
itsaky-adfa Apr 2, 2026
4129a47
feat: suggest local and imported extension functions
itsaky-adfa Apr 2, 2026
7d736fa
fix: do not suggest extension functions for scope completions
itsaky-adfa Apr 2, 2026
87234f8
feat: add scope-sensitive keyword completions
itsaky-adfa Apr 3, 2026
7d78db1
feat: add indexing api and service implementation
itsaky-adfa Apr 6, 2026
c35aa9f
fix: metadata version is sometimes not parsed
itsaky-adfa Apr 7, 2026
bede4db
fix: make JvmSymbolIndex exclusive to external libraries
itsaky-adfa Apr 8, 2026
9eb4ffe
feat: add module resolver to resolve library modules from source path
itsaky-adfa Apr 8, 2026
1586b8f
fix: remove unused onSourcesChanged func
itsaky-adfa Apr 16, 2026
85807ca
fix: remove IncrementalModificationTracker
itsaky-adfa Apr 16, 2026
abecf1a
fix: remove unused Gradle build events
itsaky-adfa Apr 16, 2026
68e316a
Merge branch 'stage' into feat/ADFA-3320-session-source-files-invalid…
itsaky-adfa Apr 18, 2026
4988c26
Merge branch 'stage' into feat/ADFA-3320-session-source-files-invalid…
itsaky-adfa Apr 20, 2026
d54e63a
Merge branch 'feat/ADFA-3320-session-source-files-invalidation' into …
itsaky-adfa Apr 20, 2026
e8f9144
Merge branch 'feat/ADFA-3320-KtFileManager' into feat/ADFA-3320-code-…
itsaky-adfa Apr 20, 2026
a9580a0
Merge branch 'feat/ADFA-3320-code-completions' into feat/ADFA-3581-index
itsaky-adfa Apr 20, 2026
f0107f6
Merge branch 'stage' into feat/ADFA-3581-index
itsaky-adfa Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,12 @@ class GradleBuildService :
buildResult = result,
),
)

buildServiceScope.launch {
ProjectManagerImpl.getInstance()
.indexingServiceManager
.onBuildCompleted()
}
}

override fun onProgressEvent(event: ProgressEvent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.itsaky.androidide.editor.schemes.LanguageSpecProvider.getLanguageSpec
import com.itsaky.androidide.editor.schemes.LocalCaptureSpecProvider.newLocalCaptureSpec
import com.itsaky.androidide.editor.utils.isNonBlankLine
import com.itsaky.androidide.treesitter.TSLanguage
import com.itsaky.androidide.treesitter.TreeSitter
import com.itsaky.androidide.utils.IntPair
import io.github.rosemoe.sora.editor.ts.TsTheme
import io.github.rosemoe.sora.lang.Language.INTERRUPTION_LEVEL_STRONG
Expand All @@ -42,156 +43,167 @@ import java.io.File
* @author Akash Yadav
*/
abstract class TreeSitterLanguage(
context: Context,
lang: TSLanguage,
private val langType: String
context: Context,
lang: TSLanguage,
private val langType: String
) : IDELanguage() {

private var languageSpec =
getLanguageSpec(context, langType, lang, newLocalCaptureSpec(langType))
private var tsTheme = TsTheme(languageSpec.spec.tsQuery)
private lateinit var _indentProvider: TreeSitterIndentProvider
private val analyzer by lazy { TreeSitterAnalyzeManager(languageSpec.spec, tsTheme) }
private val newlineHandlersLazy by lazy { createNewlineHandlers() }

private var languageScheme: LanguageScheme? = null

private val indentProvider: TreeSitterIndentProvider
get() {
if (!this::_indentProvider.isInitialized) {
this._indentProvider = TreeSitterIndentProvider(
languageSpec,
analyzer.analyzeWorker!!,
getTabSize()
)
}

return _indentProvider
}

companion object {

private val log = LoggerFactory.getLogger(TreeSitterLanguage::class.java)
private const val DEF_IDENT_ADV = 0
}

fun setupWith(scheme: IDEColorScheme?) {
val langScheme = scheme?.languages?.get(langType)
this.languageScheme = langScheme
this.analyzer.langScheme = languageScheme
langScheme?.styles?.forEach { tsTheme.putStyleRule(it.key, it.value.makeStyle()) }
}

override fun addBreakpoint(line: Int) {
this.analyzer.addBreakpoint(line)
}

override fun removeBreakpoint(line: Int) {
this.analyzer.removeBreakpoint(line)
}

override fun removeAllBreakpoints() {
this.analyzer.removeAllBreakpoints()
}

override fun toggleBreakpoint(line: Int) {
this.analyzer.toggleBreakpoint(line)
}

override fun highlightLine(line: Int) {
this.analyzer.highlightLine(line)
}

override fun unhighlightLines() {
this.analyzer.unhighlightLines()
}

override fun getAnalyzeManager(): AnalyzeManager {
return this.analyzer
}

override fun getSymbolPairs(): SymbolPairMatch {
return CommonSymbolPairs()
}

open fun createNewlineHandlers(): Array<TSBracketsHandler> {
return emptyArray()
}

override fun getNewlineHandlers(): Array<TSBracketsHandler> {
return newlineHandlersLazy
}

override fun getInterruptionLevel(): Int {
return INTERRUPTION_LEVEL_STRONG
}

override fun getIndentAdvance(
content: ContentReference,
line: Int,
column: Int,
spaceCountOnLine: Int,
tabCountOnLine: Int
): Int {
return try {
if (line == content.reference.lineCount - 1) {
// line + 1 does not exist
// TODO(itsaky): Update this implementation when this behavior is fixed in sora-editor
return DEF_IDENT_ADV
}

var linesToReq = LongArray(1)
linesToReq[0] = IntPair.pack(line, column)

if (content.reference.isNonBlankLine(line + 1)) {
// consider the indentation of the next line only if it is non-blank
linesToReq += IntPair.pack(line + 1, 0)
}

val indents = this.indentProvider.getIndentsForLines(
content = content.reference,
positions = linesToReq,
)

if (indents.size == 1) {
val indent = indents[0]
if (indent == TreeSitterIndentProvider.INDENTATION_ERR) {
return DEF_IDENT_ADV
}

return indent - (spaceCountOnLine + (tabCountOnLine * getTabSize()))
}

val (indentLine, indentNxtLine) = indents
if (indentLine == TreeSitterIndentProvider.INDENTATION_ERR
|| indentNxtLine == TreeSitterIndentProvider.INDENTATION_ERR) {
log.debug(
"expectedIndent[{}]={}, expectedIndentNextLine[{}]={}, returning default indent advance",
line, indentLine, line + 1, indentNxtLine)
return DEF_IDENT_ADV
}

return indentNxtLine - indentLine
} catch (e: Exception) {
log.error("An error occurred computing indentation at line:column::{}:{}", line, column, e)
DEF_IDENT_ADV
}

}

override fun destroy() {
this.languageSpec.close()
this.languageScheme = null
}

/** A [Factory] creates instance of a specific [TreeSitterLanguage] implementation. */
fun interface Factory<T : TreeSitterLanguage> {

/**
* Create the instance of the [TreeSitterLanguage] implementation.
*
* @param context The current context.
*/
fun create(context: Context): T
}
private var languageSpec =
getLanguageSpec(context, langType, lang, newLocalCaptureSpec(langType))
private var tsTheme = TsTheme(languageSpec.spec.tsQuery)
private lateinit var _indentProvider: TreeSitterIndentProvider
private val analyzer by lazy { TreeSitterAnalyzeManager(languageSpec.spec, tsTheme) }
private val newlineHandlersLazy by lazy { createNewlineHandlers() }

private var languageScheme: LanguageScheme? = null

private val indentProvider: TreeSitterIndentProvider
get() {
if (!this::_indentProvider.isInitialized) {
this._indentProvider = TreeSitterIndentProvider(
languageSpec,
analyzer.analyzeWorker!!,
getTabSize()
)
}

return _indentProvider
}

companion object {

init {
TreeSitter.loadLibrary()
}

private val log = LoggerFactory.getLogger(TreeSitterLanguage::class.java)
private const val DEF_IDENT_ADV = 0
}

fun setupWith(scheme: IDEColorScheme?) {
val langScheme = scheme?.languages?.get(langType)
this.languageScheme = langScheme
this.analyzer.langScheme = languageScheme
langScheme?.styles?.forEach { tsTheme.putStyleRule(it.key, it.value.makeStyle()) }
}

override fun addBreakpoint(line: Int) {
this.analyzer.addBreakpoint(line)
}

override fun removeBreakpoint(line: Int) {
this.analyzer.removeBreakpoint(line)
}

override fun removeAllBreakpoints() {
this.analyzer.removeAllBreakpoints()
}

override fun toggleBreakpoint(line: Int) {
this.analyzer.toggleBreakpoint(line)
}

override fun highlightLine(line: Int) {
this.analyzer.highlightLine(line)
}

override fun unhighlightLines() {
this.analyzer.unhighlightLines()
}

override fun getAnalyzeManager(): AnalyzeManager {
return this.analyzer
}

override fun getSymbolPairs(): SymbolPairMatch {
return CommonSymbolPairs()
}

open fun createNewlineHandlers(): Array<TSBracketsHandler> {
return emptyArray()
}

override fun getNewlineHandlers(): Array<TSBracketsHandler> {
return newlineHandlersLazy
}

override fun getInterruptionLevel(): Int {
return INTERRUPTION_LEVEL_STRONG
}

override fun getIndentAdvance(
content: ContentReference,
line: Int,
column: Int,
spaceCountOnLine: Int,
tabCountOnLine: Int
): Int {
return try {
if (line == content.reference.lineCount - 1) {
// line + 1 does not exist
// TODO(itsaky): Update this implementation when this behavior is fixed in sora-editor
return DEF_IDENT_ADV
}

var linesToReq = LongArray(1)
linesToReq[0] = IntPair.pack(line, column)

if (content.reference.isNonBlankLine(line + 1)) {
// consider the indentation of the next line only if it is non-blank
linesToReq += IntPair.pack(line + 1, 0)
}

val indents = this.indentProvider.getIndentsForLines(
content = content.reference,
positions = linesToReq,
)

if (indents.size == 1) {
val indent = indents[0]
if (indent == TreeSitterIndentProvider.INDENTATION_ERR) {
return DEF_IDENT_ADV
}

return indent - (spaceCountOnLine + (tabCountOnLine * getTabSize()))
}

val (indentLine, indentNxtLine) = indents
if (indentLine == TreeSitterIndentProvider.INDENTATION_ERR
|| indentNxtLine == TreeSitterIndentProvider.INDENTATION_ERR
) {
log.debug(
"expectedIndent[{}]={}, expectedIndentNextLine[{}]={}, returning default indent advance",
line, indentLine, line + 1, indentNxtLine
)
return DEF_IDENT_ADV
}

return indentNxtLine - indentLine
} catch (e: Exception) {
log.error(
"An error occurred computing indentation at line:column::{}:{}",
line,
column,
e
)
DEF_IDENT_ADV
}

}

override fun destroy() {
this.languageSpec.close()
this.languageScheme = null
}

/** A [Factory] creates instance of a specific [TreeSitterLanguage] implementation. */
fun interface Factory<T : TreeSitterLanguage> {

/**
* Create the instance of the [TreeSitterLanguage] implementation.
*
* @param context The current context.
*/
fun create(context: Context): T
}
}
7 changes: 4 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
activityKtx = "1.8.2"
agp = "8.8.2"
agp-tooling = "8.11.0"
appcompat = "1.6.1"
androidx-sqlite = "2.6.2"
appcompatVersion = "1.7.1"
bcprovJdk18on = "1.80"
colorpickerview = "2.3.0"
Expand Down Expand Up @@ -97,6 +97,8 @@ androidx-palette-ktx = { module = "androidx.palette:palette-ktx", version.ref =
androidx-preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceKtxVersion" }
androidx-recyclerview-v132 = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" }
androidx-sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "androidx-sqlite" }
androidx-sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "androidx-sqlite" }
androidx-viewpager2-v110beta02 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" }
bcpkix-jdk18on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bcprovJdk18on" }
bcprov-jdk18on = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "bcprovJdk18on" }
Expand All @@ -121,10 +123,8 @@ desugar_jdk_libs-v215 = { module = "com.android.tools:desugar_jdk_libs", version
google-genai = { module = "com.google.genai:google-genai", version.ref = "googleGenai" }
gson-v2101 = { module = "com.google.code.gson:gson", version.ref = "gson" }
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinAndroid" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
play-services-oss-licenses = { module = "com.google.android.gms:play-services-oss-licenses", version.ref = "playServicesOssLicenses" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }

Expand Down Expand Up @@ -311,6 +311,7 @@ monitor = { group = "androidx.test", name = "monitor", version.ref = "monitorVer
org-json = { module = "org.json:json", version = "20210307"}

pebble = { module = "io.pebbletemplates:pebble", version.ref = "pebble" }
kotlinx-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down
19 changes: 19 additions & 0 deletions lsp/indexing/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import com.itsaky.androidide.build.config.BuildConfig

plugins {
id("com.android.library")
id("kotlin-android")
}

android {
namespace = "${BuildConfig.PACKAGE_NAME}.lsp.indexing"
}

dependencies {
api(libs.androidx.annotation)
api(libs.androidx.sqlite.ktx)
api(libs.androidx.sqlite.framework)
api(libs.kotlinx.coroutines.core)

api(projects.logger)
}
Loading
Loading