Skip to content

Commit 27ef112

Browse files
committed
Merge branch 'stage' into fix/ADFA-2756
2 parents 97be304 + 90dea94 commit 27ef112

File tree

86 files changed

+23363
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+23363
-272
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,6 @@ testing/**/bin/
162162
# Compose preview downloaded JARs (generated during build)
163163
compose-preview/src/main/assets/compose/
164164

165+
# Generated stdlib index (generated at build time by :lsp:kotlin-stdlib-generator)
166+
lsp/kotlin/src/main/resources/stdlib-index.json
167+

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ dependencies {
280280
implementation(projects.logsender)
281281
implementation(projects.lsp.api)
282282
implementation(projects.lsp.java)
283+
implementation(projects.lsp.kotlin)
283284
implementation(projects.lsp.xml)
284285
implementation(projects.lexers)
285286
implementation(projects.lookup)

app/src/main/java/com/itsaky/androidide/actions/file/FormatCodeAction.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.core.content.ContextCompat
2222
import com.itsaky.androidide.actions.ActionData
2323
import com.itsaky.androidide.actions.ActionItem
2424
import com.itsaky.androidide.actions.EditorRelatedAction
25+
import com.itsaky.androidide.idetooltips.TooltipTag
2526
import com.itsaky.androidide.resources.R
2627

2728
/**
@@ -32,6 +33,8 @@ import com.itsaky.androidide.resources.R
3233
class FormatCodeAction(context: Context, override val order: Int) : EditorRelatedAction() {
3334
override val id: String = "ide.editor.code.text.format"
3435
override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS
36+
override fun retrieveTooltipTag(isReadOnlyContext: Boolean) =
37+
TooltipTag.EDITOR_TOOLBAR_FORMAT_CODE
3538

3639
init {
3740
label = context.getString(R.string.title_format_code)

app/src/main/java/com/itsaky/androidide/app/strictmode/ViolationHandler.kt

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,40 @@ import org.slf4j.LoggerFactory
99
* @author Akash Yadav
1010
*/
1111
object ViolationHandler {
12-
private val logger = LoggerFactory.getLogger(ViolationHandler::class.java)
12+
private val logger = LoggerFactory.getLogger(ViolationHandler::class.java)
1313

14-
private const val ALLOW_VIOLATION_MESSAGE = "StrictMode violation '{}' allowed because: {}"
15-
private const val WARN_VIOLATION_MESSAGE = "StrictMode violation detected"
14+
private const val ALLOW_VIOLATION_MESSAGE = "StrictMode violation '{}' allowed because: {}"
15+
private const val WARN_VIOLATION_MESSAGE = "StrictMode violation detected"
1616

17-
/**
18-
* Allow the violation.
19-
*
20-
* @param violation The violation that was detected.
21-
*/
22-
fun allow(
23-
violation: ViolationDispatcher.Violation,
24-
reason: String,
25-
) {
26-
logger.info(ALLOW_VIOLATION_MESSAGE, violation.violation.javaClass.simpleName, reason)
27-
}
17+
/**
18+
* Allow the violation.
19+
*
20+
* @param violation The violation that was detected.
21+
*/
22+
fun allow(
23+
violation: ViolationDispatcher.Violation,
24+
reason: String,
25+
) {
26+
logger.info(ALLOW_VIOLATION_MESSAGE, violation.violation.javaClass.simpleName, reason)
27+
}
2828

29-
/**
30-
* Log the violation.
31-
*
32-
* @param violation The violation that was detected.
33-
*/
34-
fun log(violation: ViolationDispatcher.Violation) {
35-
logger.warn(WARN_VIOLATION_MESSAGE, violation.violation)
36-
}
29+
/**
30+
* Log the violation.
31+
*
32+
* @param violation The violation that was detected.
33+
*/
34+
fun log(violation: ViolationDispatcher.Violation) {
35+
logger.warn(WARN_VIOLATION_MESSAGE, violation.violation)
36+
}
3737

38-
/**
39-
* Crash the app (throw an exception on the main thread).
40-
*
41-
* @param violation The violation that was detected.
42-
*/
43-
fun crash(violation: ViolationDispatcher.Violation) {
44-
runOnUiThread {
45-
throw RuntimeException(WARN_VIOLATION_MESSAGE, violation.violation)
46-
}
47-
}
48-
}
38+
/**
39+
* Crash the app (throw an exception on the main thread).
40+
*
41+
* @param violation The violation that was detected.
42+
*/
43+
fun crash(violation: ViolationDispatcher.Violation) {
44+
runOnUiThread {
45+
throw RuntimeException(WARN_VIOLATION_MESSAGE, violation.violation)
46+
}
47+
}
48+
}

app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView
2222
import com.itsaky.androidide.R
2323
import com.itsaky.androidide.adapters.DiagnosticsAdapter
2424
import com.itsaky.androidide.idetooltips.TooltipTag
25+
import com.itsaky.androidide.lsp.IDELanguageClientImpl
2526

2627
class DiagnosticsListFragment : RecyclerViewFragment<DiagnosticsAdapter>() {
2728
override val fragmentTooltipTag: String? = TooltipTag.PROJECT_DIAGNOSTICS
@@ -34,5 +35,24 @@ class DiagnosticsListFragment : RecyclerViewFragment<DiagnosticsAdapter>() {
3435
) {
3536
super.onViewCreated(view, savedInstanceState)
3637
emptyStateViewModel.setEmptyMessage(getString(R.string.msg_emptyview_diagnostics))
38+
loadExistingDiagnostics()
39+
}
40+
41+
override fun onResume() {
42+
super.onResume()
43+
loadExistingDiagnostics()
44+
}
45+
46+
private fun loadExistingDiagnostics() {
47+
if (!IDELanguageClientImpl.isInitialized()) {
48+
return
49+
}
50+
51+
val client = IDELanguageClientImpl.getInstance()
52+
val activity = activity ?: return
53+
54+
if (activity is com.itsaky.androidide.interfaces.DiagnosticClickListener) {
55+
setAdapter(client.newDiagnosticsAdapter())
56+
}
3757
}
3858
}

app/src/main/java/com/itsaky/androidide/handlers/LspHandler.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import com.itsaky.androidide.lsp.api.ILanguageClient
2121
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
2222
import com.itsaky.androidide.lsp.debug.IDebugClient
2323
import com.itsaky.androidide.lsp.java.JavaLanguageServer
24+
import com.itsaky.androidide.lsp.kotlin.KotlinLanguageServer
2425
import com.itsaky.androidide.lsp.xml.XMLLanguageServer
26+
import com.itsaky.androidide.utils.FeatureFlags
2527

2628
/**
2729
*
@@ -32,6 +34,9 @@ object LspHandler {
3234
fun registerLanguageServers() {
3335
ILanguageServerRegistry.getDefault().apply {
3436
getServer(JavaLanguageServer.SERVER_ID) ?: register(JavaLanguageServer())
37+
if (FeatureFlags.isExperimentsEnabled) {
38+
getServer(KotlinLanguageServer.SERVER_ID) ?: register(KotlinLanguageServer())
39+
}
3540
getServer(XMLLanguageServer.SERVER_ID) ?: register(XMLLanguageServer())
3641
}
3742
}

app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ public DiagnosticItem getDiagnosticAt(final File file, final int line, final int
171171
return DiagnosticUtil.binarySearchDiagnostic(this.diagnostics.get(file), line, column);
172172
}
173173

174+
@NonNull
175+
public Map<File, List<DiagnosticItem>> getAllDiagnostics() {
176+
return new HashMap<>(this.diagnostics);
177+
}
178+
174179
@Override
175180
public void performCodeAction(PerformCodeActionParams params) {
176181
if (params == null) {

app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package com.itsaky.androidide.ui
1919

2020
import android.app.Activity
21+
import android.content.ClipData
22+
import android.content.ClipboardManager
2123
import android.content.Context
2224
import android.util.AttributeSet
2325
import android.util.Log
@@ -59,7 +61,10 @@ import com.itsaky.androidide.models.LogLine
5961
import com.itsaky.androidide.resources.R.string
6062
import com.itsaky.androidide.utils.IntentUtils.shareFile
6163
import com.itsaky.androidide.utils.Symbols.forFile
64+
import com.itsaky.androidide.utils.DiagnosticsFormatter
6265
import com.itsaky.androidide.utils.flashError
66+
import com.itsaky.androidide.utils.flashSuccess
67+
import com.itsaky.androidide.lsp.IDELanguageClientImpl
6368
import com.itsaky.androidide.viewmodel.ApkInstallationViewModel
6469
import com.itsaky.androidide.viewmodel.BottomSheetViewModel
6570
import kotlinx.coroutines.Dispatchers
@@ -180,6 +185,12 @@ constructor(
180185
binding.clearFab.hide()
181186
binding.shareOutputFab.hide()
182187
}
188+
189+
if (tab.position == EditorBottomSheetTabAdapter.TAB_DIAGNOSTICS) {
190+
binding.copyDiagnosticsFab.show()
191+
} else {
192+
binding.copyDiagnosticsFab.hide()
193+
}
183194
}
184195

185196
override fun onTabUnselected(tab: Tab) {}
@@ -232,6 +243,10 @@ constructor(
232243
}
233244
binding.clearFab.setOnLongClickListener(generateTooltipListener(TooltipTag.OUTPUT_CLEAR))
234245

246+
binding.copyDiagnosticsFab.setOnClickListener {
247+
copyDiagnosticsToClipboard()
248+
}
249+
235250
binding.headerContainer.setOnClickListener {
236251
viewModel.setSheetState(sheetState = BottomSheetBehavior.STATE_EXPANDED)
237252
}
@@ -255,6 +270,7 @@ constructor(
255270
binding.shareOutputFab.setOnLongClickListener(null)
256271
binding.clearFab.setOnClickListener(null)
257272
binding.clearFab.setOnLongClickListener(null)
273+
binding.copyDiagnosticsFab.setOnClickListener(null)
258274
binding.headerContainer.setOnClickListener(null)
259275
ViewCompat.setOnApplyWindowInsetsListener(this, null)
260276

@@ -517,4 +533,28 @@ constructor(
517533

518534
return path.toFile()
519535
}
536+
537+
private fun copyDiagnosticsToClipboard() {
538+
if (!IDELanguageClientImpl.isInitialized()) {
539+
flashError(context.getString(string.msg_no_diagnostics_to_copy))
540+
return
541+
}
542+
543+
val diagnostics = IDELanguageClientImpl.getInstance().allDiagnostics
544+
if (diagnostics.isEmpty()) {
545+
flashError(context.getString(string.msg_no_diagnostics_to_copy))
546+
return
547+
}
548+
549+
val formatted = DiagnosticsFormatter.format(diagnostics)
550+
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
551+
if (clipboard == null) {
552+
flashError(context.getString(string.msg_clipboard_copy_failed))
553+
return
554+
}
555+
556+
runCatching { clipboard.setPrimaryClip(ClipData.newPlainText("diagnostics", formatted)) }
557+
.onSuccess { flashSuccess(context.getString(string.msg_diagnostics_copied)) }
558+
.onFailure { flashError(context.getString(string.msg_clipboard_copy_failed)) }
559+
}
520560
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
2+
3+
package com.itsaky.androidide.utils
4+
5+
import com.itsaky.androidide.lsp.models.DiagnosticItem
6+
import com.itsaky.androidide.lsp.models.DiagnosticSeverity
7+
import java.io.File
8+
import java.text.SimpleDateFormat
9+
import java.util.Date
10+
import java.util.Locale
11+
12+
object DiagnosticsFormatter {
13+
14+
private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
15+
16+
fun format(diagnostics: Map<File, List<DiagnosticItem>>): String {
17+
if (diagnostics.isEmpty()) return "No diagnostics"
18+
19+
val builder = StringBuilder()
20+
builder.appendLine("=== Diagnostics Report ===")
21+
builder.appendLine("Generated: ${dateFormat.format(Date())}")
22+
builder.appendLine()
23+
24+
var totalErrors = 0
25+
var totalWarnings = 0
26+
27+
for ((file, items) in diagnostics.entries.sortedBy { it.key.name }) {
28+
if (items.isEmpty()) continue
29+
30+
val errors = items.count { it.severity == DiagnosticSeverity.ERROR }
31+
val warnings = items.count { it.severity == DiagnosticSeverity.WARNING }
32+
totalErrors += errors
33+
totalWarnings += warnings
34+
35+
builder.appendLine("--- ${file.name} ($errors errors, $warnings warnings) ---")
36+
builder.appendLine("Path: ${file.absolutePath}")
37+
builder.appendLine()
38+
39+
for (item in items.sortedBy { it.range.start.line }) {
40+
val severity = when (item.severity) {
41+
DiagnosticSeverity.ERROR -> "ERROR"
42+
DiagnosticSeverity.WARNING -> "WARNING"
43+
DiagnosticSeverity.INFO -> "INFO"
44+
DiagnosticSeverity.HINT -> "HINT"
45+
}
46+
47+
val line = item.range.start.line + 1
48+
val column = item.range.start.column + 1
49+
50+
builder.appendLine(" [$severity] Line $line:$column")
51+
builder.appendLine(" ${item.message}")
52+
if (item.code.isNotEmpty()) {
53+
builder.appendLine(" Code: ${item.code}")
54+
}
55+
builder.appendLine()
56+
}
57+
}
58+
59+
builder.appendLine("=== Summary ===")
60+
builder.appendLine("Total: $totalErrors errors, $totalWarnings warnings")
61+
builder.appendLine("Files: ${diagnostics.count { it.value.isNotEmpty() }}")
62+
63+
return builder.toString()
64+
}
65+
}

app/src/main/res/layout/layout_editor_bottom_sheet.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,17 @@
9898
android:layout_marginBottom="16dp"
9999
android:src="@drawable/ic_share" />
100100

101+
<com.google.android.material.floatingactionbutton.FloatingActionButton
102+
android:id="@+id/copy_diagnostics_fab"
103+
android:layout_width="wrap_content"
104+
android:layout_height="wrap_content"
105+
android:layout_above="@id/share_output_fab"
106+
android:layout_alignParentEnd="true"
107+
android:layout_marginStart="16dp"
108+
android:layout_marginTop="16dp"
109+
android:layout_marginEnd="16dp"
110+
android:layout_marginBottom="16dp"
111+
android:src="@drawable/ic_copy"
112+
android:visibility="gone" />
113+
101114
</RelativeLayout>

0 commit comments

Comments
 (0)