Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
50 changes: 50 additions & 0 deletions .github/workflows/build.yml
Comment thread
arun28082007 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Build Debug APK

on:
push:
branches: [ "memory" ]
workflow_dispatch:



jobs:
build:
if: github.repository == 'arun28082007/Cosmic-IDE'
runs-on: macos-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Zig
uses: mlugg/setup-zig@v2

- name: Set up JDK 17
uses: actions/setup-java@v4.2.1
with:
distribution: 'temurin'
java-version: '17'

- name: Install patch tools
run: brew install binutils jq ncurses zstd gnu-tar python

- name: Generate glibc.tar.zst
run: bash ./scripts/build-glibc.sh

- name: Give permission to executable
run: chmod +x gradlew

- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Build with gradle
uses: gradle/gradle-build-action@v3.1.0
with:
gradle-version: nightly
arguments: assembleProdDebug

- name: Upload debug apks
uses: actions/upload-artifact@v4
with:
name: app-arm64-v8a
path: app/build/outputs/apk/prod/debug/app-prod-arm64-v8a-debug.apk
18 changes: 18 additions & 0 deletions app/src/main/kotlin/org/cosmicide/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.cosmicide.common.Analytics
import org.cosmicide.common.IndexManager
import org.cosmicide.common.MemoryMonitor
import org.cosmicide.common.MemoryUtils
import org.cosmicide.common.Prefs
import org.cosmicide.editor.lsp.handleLspShowDocument
import org.cosmicide.plugin.CosmicPluginHost
Expand Down Expand Up @@ -81,6 +84,8 @@ import java.util.TimeZone

class App : Application() {

private lateinit var memoryMonitor: MemoryMonitor

companion object {

/**
Expand Down Expand Up @@ -117,6 +122,8 @@ class App : Application() {
instance = WeakReference(this)
HookManager.context = WeakReference(this)

IndexManager.init(this)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
HiddenApiBypass.addHiddenApiExemptions()
}
Expand All @@ -125,13 +132,24 @@ class App : Application() {

CosmicPluginHost.init(this)

memoryMonitor = MemoryMonitor(intervalMs = 5000L) { pressure ->
if (pressure == MemoryUtils.MemoryPressure.CRITICAL) {
android.util.Log.w("App", "Critical memory pressure: throttling LSP servers")
}
}
memoryMonitor.start()

Analytics.setAnalyticsCollectionEnabled(Prefs.analyticsEnabled)
applyLSP4JHooks()

}

override fun onTerminate() {
super.onTerminate()
if (::memoryMonitor.isInitialized) {
memoryMonitor.stop()
}
IndexManager.shutdown()
}

fun loadTextmateTheme() {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/kotlin/org/cosmicide/common/AppDispatchers.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.cosmicide.common

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher

object AppDispatchers {
val IO = MemoryAwareDispatchers.IO.asCoroutineDispatcher()
val Main = Dispatchers.Main
}
Comment thread
arun28082007 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// MemoryAwareDispatchers lives in common module.
// Use org.cosmicide.common.MemoryAwareDispatchers.IO (ExecutorService).
61 changes: 22 additions & 39 deletions app/src/main/kotlin/org/cosmicide/editor/lsp/LspEditorAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.cosmicide.common.AppDispatchers
import org.cosmicide.common.IndexManager
import org.cosmicide.editor.LspServerConnection
import org.cosmicide.editor.LspServerDefinition
import org.cosmicide.editor.LspServerRequest
Expand All @@ -45,9 +47,6 @@ import java.io.InputStream
import java.io.OutputStream
import java.net.URI
import java.net.URL
import java.nio.file.AtomicMoveNotSupportedException
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.security.MessageDigest
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap
Expand All @@ -65,7 +64,7 @@ fun CodeEditor.configureLspLanguage(
val lspProject = LspProjects.forRoot(request.project.root.absolutePath)
ensureRequestTimeouts()

CoroutineScope(Dispatchers.IO).launch {
CoroutineScope(AppDispatchers.IO).launch {
ensureInitializationTimeout(definition.initializationTimeoutMillis)
val fileExtensions = definition.fileExtensions.toList()
val requestedFileExtension = fileExtensions.firstOrNull {
Expand Down Expand Up @@ -176,7 +175,7 @@ fun CodeEditor.configureLspLanguage(

fun CodeEditor.disposeLspLanguage() {
val lspEditors = LspProjects.editorsFor(this)
CoroutineScope(Dispatchers.IO).launch {
CoroutineScope(AppDispatchers.IO).launch {
lspEditors.forEach(LspEditor::dispose)
}
}
Expand Down Expand Up @@ -448,23 +447,14 @@ private fun createTextMateLanguage(
return createTextMateLanguage(definition, grammarText)
}

val cacheFile = grammarCacheFile(context, grammarLink)
var cachedGrammarText: String? = null
if (cacheFile.isFile) {
cachedGrammarText = runCatching { cacheFile.inputStream().readGrammarText() }
.onFailure {
Log.w(TAG, "Discarding unreadable grammar cache ${cacheFile.name}", it)
cacheFile.delete()
}
.getOrNull()
}
var cachedGrammarText: String? = readGrammarViaIndexManager(grammarLink)

if (cachedGrammarText != null && isTextMateGrammarCacheFresh(cacheFile)) {
if (cachedGrammarText != null) {
try {
return createTextMateLanguage(definition, cachedGrammarText)
} catch (e: Exception) {
Log.w(TAG, "Discarding invalid grammar cache ${cacheFile.name}", e)
cacheFile.delete()
Log.w(TAG, "Discarding invalid grammar cache for $grammarLink", e)
IndexManager.invalidateProject("grammar:$grammarLink")
cachedGrammarText = null
}
}
Expand All @@ -479,7 +469,7 @@ private fun createTextMateLanguage(

return try {
createTextMateLanguage(definition, refreshedGrammarText).also {
runCatching { cacheGrammar(cacheFile, refreshedGrammarText) }
runCatching { cacheGrammarViaIndexManager(grammarLink, refreshedGrammarText) }
.onFailure { error ->
Log.w(TAG, "Unable to cache grammar from $grammarLink", error)
}
Expand Down Expand Up @@ -552,27 +542,20 @@ private fun grammarCacheFile(context: Context, grammarLink: String): File {
.resolve("$cacheKey.grammar")
}

private fun cacheGrammar(cacheFile: File, grammarText: String) {
val temporaryFile = File.createTempFile(cacheFile.name, ".tmp", cacheFile.parentFile)
try {
temporaryFile.writeText(grammarText, Charsets.UTF_8)
try {
Files.move(
temporaryFile.toPath(),
cacheFile.toPath(),
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING
)
} catch (_: AtomicMoveNotSupportedException) {
Files.move(
temporaryFile.toPath(),
cacheFile.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
private fun cacheGrammarViaIndexManager(grammarLink: String, grammarText: String) {
val key = "grammar:${grammarLink}"
IndexManager.getOrBuildIndex(key, "text") { grammarText.toByteArray(Charsets.UTF_8) }
}

private fun readGrammarViaIndexManager(grammarLink: String): String? {
val key = "grammar:${grammarLink}"
return try {
val segment = IndexManager.getOrBuildIndex(key, "text") {
throw IllegalStateException("Grammar not cached: $grammarLink")
}
cacheFile.setLastModified(System.currentTimeMillis())
} finally {
temporaryFile.delete()
String(segment.readBlock(0, segment.size.toInt()), Charsets.UTF_8)
} catch (e: Exception) {
null
}
}

Expand Down
7 changes: 6 additions & 1 deletion app/src/main/kotlin/org/cosmicide/model/ProjectRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.cosmicide.project.Project
import org.cosmicide.project.ProjectTypeProvider
import org.cosmicide.util.FileUtil
import java.io.File
import java.util.concurrent.ConcurrentHashMap

interface ProjectRepository {
fun projects(): List<Project>
Expand All @@ -17,13 +18,16 @@ internal class FileSystemProjectRepository(
private val projectTypeProviders: () -> List<ProjectTypeProvider> = { emptyList() }
) : ProjectRepository {
private val root = projectsDirectory.canonicalFile
private val languageCache = ConcurrentHashMap<String, Language>()

override fun projects(): List<Project> = root
.listFiles { file -> file.isDirectory }
?.sortedByDescending(File::lastModified)
.orEmpty()
.map { projectRoot ->
Project(projectRoot, detectLanguage(projectRoot))
Project(projectRoot, languageCache.getOrPut(projectRoot.absolutePath) {
detectLanguage(projectRoot)
})
}

override fun delete(project: Project) {
Expand All @@ -32,6 +36,7 @@ internal class FileSystemProjectRepository(
"Projects can only be deleted from the configured projects directory"
}
require(target.isDirectory) { "Project no longer exists: ${project.name}" }
languageCache.remove(project.root.absolutePath)
check(target.deleteRecursively()) { "Could not delete ${project.name}" }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.cosmicide.common.AppDispatchers
import org.cosmicide.common.Prefs
import org.cosmicide.exec.ProcessExecutor
import org.cosmicide.exec.linux.LinuxProcessRunner
Expand Down Expand Up @@ -44,15 +45,15 @@ internal class AndroidCommandExecutionService(context: Context) :
override suspend fun execute(
request: CommandRequest,
onOutput: (String) -> Unit
): CommandResult = withContext(Dispatchers.IO) {
): CommandResult = withContext(AppDispatchers.IO) {
require(request.workingDirectory.isDirectory) {
"Working directory does not exist: ${request.workingDirectory.absolutePath}"
}

val process = start(request, redirectErrorStream = true)
val captured = StringBuilder()
val callerJob = currentCoroutineContext()[Job]
val cancellationWatcher = CoroutineScope(Dispatchers.IO).launch {
val cancellationWatcher = CoroutineScope(AppDispatchers.IO).launch {
while (callerJob?.isActive == true) delay(CANCELLATION_POLL_MILLIS.milliseconds)
if (callerJob?.isCancelled == true && process.isAlive) process.destroy()
}
Expand Down
23 changes: 18 additions & 5 deletions app/src/main/kotlin/org/cosmicide/plugin/CosmicPluginHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ object CosmicPluginHost {
@Volatile
private var initialized = false

@Volatile
private var pluginsLoaded = false

var pluginManager: AndroidPluginManager? = null
private set

Expand Down Expand Up @@ -76,23 +79,33 @@ object CosmicPluginHost {
throwable
)
}
manager.loadInstalledPlugins().forEach { result ->
result.onFailure { descriptorId, reason, throwable ->
Log.w(TAG, "Failed to load plugin $descriptorId: $reason", throwable)
}
}
}

initialized = true
}
}

private fun ensurePluginsLoaded() {
if (pluginsLoaded) return
synchronized(this) {
if (pluginsLoaded) return
pluginManager?.loadInstalledPlugins()?.forEach { result ->
result.onFailure { descriptorId, reason, throwable ->
Log.w(TAG, "Failed to load plugin $descriptorId: $reason", throwable)
}
}
pluginsLoaded = true
}
}

fun <T> enabledExtensions(point: org.cosmicide.plugin.api.ExtensionPoint<T>): List<T>
where T : Any, T : ConfigurableExtension {
ensurePluginsLoaded()
return extensionRegistry.extensions(point).filter(extensionSettings::isEnabled)
}

fun configurableExtensions(): List<ExtensionSettingsItem> {
ensurePluginsLoaded()
return buildList {
addRegistrations(EditorExtensionPoints.LANGUAGE_PROVIDER, "Editor languages")
addRegistrations(EditorExtensionPoints.LSP_SERVER_PROVIDER, "Language servers")
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/kotlin/org/cosmicide/plugin/PluginMarketplace.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.google.gson.JsonObject
import com.google.gson.JsonParser
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.cosmicide.common.AppDispatchers
import org.cosmicide.plugin.api.PluginDescriptor
import org.cosmicide.plugin.api.PluginHandle
import org.cosmicide.plugin.api.PluginLoadResult
Expand Down Expand Up @@ -51,13 +52,13 @@ class PluginMarketplace(
private val appContext = context.applicationContext

suspend fun fetch(repositoryUrl: String): List<PluginRepositoryEntry> =
withContext(Dispatchers.IO) {
withContext(AppDispatchers.IO) {
val json = downloadText(repositoryUrl, MAX_INDEX_BYTES)
parsePluginRepository(json)
}

suspend fun install(entry: PluginRepositoryEntry): PluginInstallResult =
withContext(Dispatchers.IO) {
withContext(AppDispatchers.IO) {
val pluginManager = checkNotNull(manager()) { "Plugin runtime is not initialized" }
val pluginRoot = FileUtil.pluginDir.apply { mkdirs() }
val token = UUID.randomUUID().toString()
Expand Down Expand Up @@ -120,7 +121,7 @@ class PluginMarketplace(
}
}

suspend fun uninstall(pluginId: String) = withContext(Dispatchers.IO) {
suspend fun uninstall(pluginId: String) = withContext(AppDispatchers.IO) {
val pluginManager = checkNotNull(manager()) { "Plugin runtime is not initialized" }
val target = FileUtil.pluginDir.resolve(pluginId)
require(target.isDirectory) {
Expand Down
Loading
Loading