Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import com.google.android.material.textview.MaterialTextView
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.activities.editor.HelpActivity
import com.itsaky.androidide.idetooltips.TooltipTag
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
import com.itsaky.androidide.lsp.java.JavaLanguageServer
import com.itsaky.androidide.lsp.java.debug.JdwpOptions
import com.itsaky.androidide.projects.IProjectManager
import com.itsaky.androidide.projects.isPluginProject
Expand All @@ -44,10 +46,10 @@ class DebugAction(
context: Context,
override val order: Int,
) : AbstractRunAction(
context = context,
labelRes = R.string.action_start_debugger,
iconRes = R.drawable.ic_db_startdebugger,
) {
context = context,
labelRes = R.string.action_start_debugger,
iconRes = R.drawable.ic_db_startdebugger,
) {
override val id = ID

override fun retrieveTooltipTag(isReadOnlyContext: Boolean) = TooltipTag.EDITOR_TOOLBAR_DEBUG
Expand Down Expand Up @@ -80,6 +82,16 @@ class DebugAction(
return false
}

val javaLsp = ILanguageServerRegistry.default
.getServer(JavaLanguageServer.SERVER_ID)
if (javaLsp?.debugAdapter?.isReady != true
) {
withContext(Dispatchers.Main.immediate) {
showDebuggerNotReadyMessage(activity)
}
return false
}

if (!canShowPairingNotification(activity)) {
withContext(Dispatchers.Main.immediate) {
showNotificationPermissionDialog(activity)
Expand Down Expand Up @@ -190,7 +202,7 @@ class DebugAction(
val nm = context.getSystemService(NotificationManager::class.java)
val channel = nm.getNotificationChannel(AdbPairingService.NOTIFICATION_CHANNEL)
return nm.areNotificationsEnabled() &&
(channel == null || channel.importance != NotificationManager.IMPORTANCE_NONE)
(channel == null || channel.importance != NotificationManager.IMPORTANCE_NONE)
}

private fun showNotificationPermissionDialog(context: Context): AlertDialog? =
Expand All @@ -215,4 +227,14 @@ class DebugAction(
}.setNegativeButton(android.R.string.cancel) { dialog, _ ->
dialog.dismiss()
}.show()

private fun showDebuggerNotReadyMessage(context: Context) =
DialogUtils
.newMaterialDialogBuilder(context)
.setMessage(
context.getString(R.string.debugger_not_ready) + System.lineSeparator()
.repeat(2) + context.getString(R.string.debugger_error_suggestion_network_restriction)
)
.setPositiveButton(android.R.string.ok, null)
.show()
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import com.itsaky.androidide.idetooltips.TooltipManager
import com.itsaky.androidide.idetooltips.TooltipTag
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.lsp.IDELanguageClientImpl
import com.itsaky.androidide.lsp.debug.DebugClientConnectionResult
import com.itsaky.androidide.lsp.java.utils.CancelChecker
import com.itsaky.androidide.projects.ProjectManagerImpl
import com.itsaky.androidide.projects.builder.BuildService
Expand Down Expand Up @@ -95,13 +96,16 @@ import com.itsaky.androidide.viewmodel.BuildViewModel
import io.github.rosemoe.sora.text.ICUUtils
import io.github.rosemoe.sora.util.IntPair
import io.sentry.Sentry
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.adfa.constants.CONTENT_KEY
import org.koin.android.ext.android.inject
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileNotFoundException
import java.net.SocketException
import java.nio.file.NoSuchFileException
import java.util.concurrent.CompletableFuture
import java.util.regex.Pattern
Expand Down Expand Up @@ -170,6 +174,8 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() {
private val buildServiceConnection = GradleBuildServiceConnnection()

companion object {
private val logger = LoggerFactory.getLogger(ProjectHandlerActivity::class.java)

const val STATE_KEY_FROM_SAVED_INSTANACE = "ide.editor.isFromSavedInstance"
const val STATE_KEY_SHOULD_INITIALIZE = "ide.editor.isInitializing"
}
Expand Down Expand Up @@ -449,7 +455,9 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() {
log.error("Gradle build service doesn't exist or the IDE is not allowed to access it.")
}

initLspClient()
lifecycleScope.launch {
initLspClient()
}
}

fun initializeProject(forceSync: Boolean = false) {
Expand Down Expand Up @@ -956,12 +964,67 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() {
startActivity(intent)
}

private fun initLspClient() {
private suspend fun initLspClient() {
if (!IDELanguageClientImpl.isInitialized()) {
IDELanguageClientImpl.initialize(this as EditorHandlerActivity)
}

connectClient(IDELanguageClientImpl.getInstance())
connectDebugClient(debuggerViewModel.debugClient)

val results = try {
connectDebugClient(debuggerViewModel.debugClient).values
} catch (e: Throwable) {
if (e is CancellationException) {
throw e
}

Sentry.captureException(e)
logger.error("Unable to connect LSP servers with debug client", e)
listOf(DebugClientConnectionResult.Failure(cause = e))
}

if (results.any { it is DebugClientConnectionResult.Failure }) {
// one or more debug adapters failed to initialize
val message = buildString {
results.filterIsInstance<DebugClientConnectionResult.Failure>().forEach { result ->
val msg = result.contextRes?.let(::getString)
?: result.context
?: (result.cause as? SocketException?).let { err ->
val msg = err?.message ?: ""
when {
msg.contains("EPERM") -> getString(string.debugger_error_errno_eperm)
msg.contains("ECONNREFUSED") -> getString(string.debugger_error_errno_econnrefused)
else -> null
}
}
?: (result.cause as? ErrnoException? ?: result.cause?.cause as? ErrnoException?)?.let { err ->
when (err.errno) {
OsConstants.EPERM -> getString(string.debugger_error_errno_eperm)
OsConstants.ECONNREFUSED -> getString(string.debugger_error_errno_econnrefused)
else -> getString(R.string.debugger_error_errno, err.errno)
}
}
?: getString(R.string.debugger_error_debugger_startup_failure)

append(msg)
append(System.lineSeparator())
}

if (isNotBlank()) {
append(System.lineSeparator())
}

append(getString(R.string.debugger_error_suggestion_network_restriction))
}

withContext(Dispatchers.Main) {
newMaterialDialogBuilder(this@ProjectHandlerActivity)
.setTitle(R.string.debugger_error_network_access_error)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.show()
Comment on lines +986 to +1025
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't show the network-access dialog for every debug startup failure.

This branch always uses the network-access title and appends the restriction suggestion, even when the classifier falls back to the generic startup message on Line 1007. That will misdiagnose non-network failures as local-network denial. Only use the network-specific copy when one of the failures was actually identified as EPERM / network-related.

}
}
}

open fun getProgressSheet(msg: Int): ProgressSheet? {
Expand Down
44 changes: 22 additions & 22 deletions app/src/main/java/com/itsaky/androidide/handlers/LspHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,28 @@ import com.itsaky.androidide.utils.FeatureFlags
*/
object LspHandler {

fun registerLanguageServers() {
ILanguageServerRegistry.getDefault().apply {
getServer(JavaLanguageServer.SERVER_ID) ?: register(JavaLanguageServer())
if (FeatureFlags.isExperimentsEnabled) {
getServer(KotlinLanguageServer.SERVER_ID) ?: register(KotlinLanguageServer())
}
getServer(XMLLanguageServer.SERVER_ID) ?: register(XMLLanguageServer())
}
}

fun connectClient(client: ILanguageClient) {
ILanguageServerRegistry.getDefault().connectClient(client)
}
fun registerLanguageServers() {
ILanguageServerRegistry.default.apply {
getServer(JavaLanguageServer.SERVER_ID) ?: register(JavaLanguageServer())
if (FeatureFlags.isExperimentsEnabled) {
getServer(KotlinLanguageServer.SERVER_ID) ?: register(KotlinLanguageServer())
}
getServer(XMLLanguageServer.SERVER_ID) ?: register(XMLLanguageServer())
}
}

fun connectDebugClient(client: IDebugClient) {
ILanguageServerRegistry.getDefault().connectDebugClient(client)
}
fun connectClient(client: ILanguageClient) {
ILanguageServerRegistry.default.connectClient(client)
}

fun destroyLanguageServers(isConfigurationChange: Boolean) {
if (isConfigurationChange) {
return
}
ILanguageServerRegistry.getDefault().destroy()
}
@Throws(Throwable::class)
suspend fun connectDebugClient(client: IDebugClient) =
ILanguageServerRegistry.default.connectDebugClient(client)

fun destroyLanguageServers(isConfigurationChange: Boolean) {
if (isConfigurationChange) {
return
}
ILanguageServerRegistry.default.destroy()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ class CodeEditorView(
else -> return null
}

return ILanguageServerRegistry.getDefault().getServer(serverID)
return ILanguageServerRegistry.default.getServer(serverID)
}

private fun configureEditorIfNeeded() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class JavaLanguage(context: Context) :
}

override val languageServer: ILanguageServer?
get() = ILanguageServerRegistry.getDefault().getServer(JavaLanguageServer.SERVER_ID)
get() = ILanguageServerRegistry.default.getServer(JavaLanguageServer.SERVER_ID)

override fun checkIsCompletionChar(c: Char): Boolean {
return MyCharacter.isJavaIdentifierPart(c) || c == '.'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package com.itsaky.androidide.editor.language.treesitter
import android.content.Context
import com.itsaky.androidide.editor.language.newline.TSBracketsHandler
import com.itsaky.androidide.editor.language.newline.TSCStyleBracketsHandler
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguage.Factory
import com.itsaky.androidide.editor.language.utils.CommonSymbolPairs
import com.itsaky.androidide.lsp.api.ILanguageServer
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
Expand All @@ -46,7 +45,7 @@ open class KotlinLanguage(context: Context) :
}

override val languageServer: ILanguageServer?
get() = ILanguageServerRegistry.getDefault().getServer(KotlinLanguageServer.SERVER_ID)
get() = ILanguageServerRegistry.default.getServer(KotlinLanguageServer.SERVER_ID)

override fun checkIsCompletionChar(c: Char): Boolean {
return MyCharacter.isJavaIdentifierPart(c) || c == '.'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class XMLLanguage(context: Context) :
TreeSitterLanguage(context, lang = TSLanguageXml.getInstance(), langType = TS_TYPE) {

override val languageServer: ILanguageServer?
get() = ILanguageServerRegistry.getDefault().getServer(XMLLanguageServer.SERVER_ID)
get() = ILanguageServerRegistry.default.getServer(XMLLanguageServer.SERVER_ID)

companion object {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,12 +309,12 @@ open class EditorActionsMenu(val editor: IDEEditor) :
data.put(com.itsaky.androidide.models.Range::class.java, editor.cursorLSPRange)
data.put(
JavaLanguageServer::class.java,
ILanguageServerRegistry.getDefault().getServer(JavaLanguageServer.SERVER_ID)
ILanguageServerRegistry.default.getServer(JavaLanguageServer.SERVER_ID)
as? JavaLanguageServer?
)
data.put(
XMLLanguageServer::class.java,
ILanguageServerRegistry.getDefault().getServer(XMLLanguageServer.SERVER_ID)
ILanguageServerRegistry.default.getServer(XMLLanguageServer.SERVER_ID)
as? XMLLanguageServer?
)
data.put(TextTarget::class.java, IdeEditorAdapter(this.editor))
Expand Down
Loading
Loading