From 2aa09ea1dff93e3681786c1cbe84c5a1d935f229 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Thu, 13 Jun 2024 21:42:41 +0400 Subject: [PATCH] Trim the settings content to the relevant lines --- .../client/core/gradle/dcl/DocumentUtils.kt | 9 ++ .../client/ui/composables/SourcesColumn.kt | 124 ++++++++++++++++++ .../actions/GetDeclarativeDocuments.kt | 82 ++---------- 3 files changed, 145 insertions(+), 70 deletions(-) create mode 100644 gradle-client/src/jvmMain/kotlin/org/gradle/client/core/gradle/dcl/DocumentUtils.kt create mode 100644 gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/composables/SourcesColumn.kt diff --git a/gradle-client/src/jvmMain/kotlin/org/gradle/client/core/gradle/dcl/DocumentUtils.kt b/gradle-client/src/jvmMain/kotlin/org/gradle/client/core/gradle/dcl/DocumentUtils.kt new file mode 100644 index 0000000..6e47bf2 --- /dev/null +++ b/gradle-client/src/jvmMain/kotlin/org/gradle/client/core/gradle/dcl/DocumentUtils.kt @@ -0,0 +1,9 @@ +package org.gradle.client.core.gradle.dcl + +import org.gradle.internal.declarativedsl.dom.DeclarativeDocument + +fun DeclarativeDocument.relevantRange(): IntRange { + val first = content.firstOrNull() ?: return IntRange.EMPTY + val last = content.last() + return IntRange(first.sourceData.indexRange.first, last.sourceData.indexRange.last) +} \ No newline at end of file diff --git a/gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/composables/SourcesColumn.kt b/gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/composables/SourcesColumn.kt new file mode 100644 index 0000000..7600a46 --- /dev/null +++ b/gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/composables/SourcesColumn.kt @@ -0,0 +1,124 @@ +package org.gradle.client.ui.composables + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.* +import org.gradle.client.ui.theme.spacing + +internal data class SourceFileViewInput( + val fileIdentifier: String, + val fileContent: String, + val relevantIndicesRange: IntRange? +) + +@Composable +internal fun SourcesColumn( + sources: List, + highlightedSourceRangeByFileId: MutableState> +) { + Column { + val sourceFileData by derivedStateOf { + sources.map { (identifier, content, relevantIndices) -> + val highlightedRangeOrNull = highlightedSourceRangeByFileId.value[identifier] + val highlightedString = sourceFileAnnotatedString(highlightedRangeOrNull, content) + val relevantHighlightedString = relevantIndices?.let { range -> + val lineBreakBeforeFocus = + highlightedString.text.take(relevantIndices.first).indexOfLast { it == '\n' } + 1 + + trimIndentationWhitespaces( + highlightedString.subSequence(TextRange(lineBreakBeforeFocus, range.last + 1)) + ) + } + SourceFileData( + identifier, + highlightedString, + relevantHighlightedString + ) + } + } + + sourceFileData.forEach { data -> + SourceFileTitleAndText(data.relativePath, data.annotatedSource, data.trimmedSource) + MaterialTheme.spacing.VerticalLevel4() + } + } +} + + + +private data class SourceFileData( + val relativePath: String, + val annotatedSource: AnnotatedString, + val trimmedSource: AnnotatedString? +) + +private fun sourceFileAnnotatedString( + highlightedSourceRange: IntRange?, + fileContent: String +) = buildAnnotatedString { + when { + highlightedSourceRange == null -> append(fileContent) + + else -> { + append(fileContent.substring(0, highlightedSourceRange.first)) + withStyle(style = SpanStyle(background = Color.Yellow)) { + append(fileContent.substring(highlightedSourceRange)) + } + append(fileContent.substring(highlightedSourceRange.last + 1)) + } + } +} + +@Composable +private fun SourceFileTitleAndText( + fileRelativePath: String, + highlightedSource: AnnotatedString, + trimmedSource: AnnotatedString? +) { + if (trimmedSource != null) { + var isTrimmed by remember { mutableStateOf(true) } + + TitleMedium(fileRelativePath) + + val linesTrimmed = highlightedSource.text.lines().count() - trimmedSource.text.lines().count() + val text = if (isTrimmed) + "($linesTrimmed irrelevant lines omitted, click to show)" + else "(hide irrelevant lines)" + + TextButton(onClick = { + isTrimmed = !isTrimmed + }) { + Text(text, modifier = Modifier.alpha(0.5f)) + } + + MaterialTheme.spacing.VerticalLevel2() + CodeBlock(Modifier.fillMaxWidth(), if (isTrimmed) trimmedSource else highlightedSource) + } else { + TitleMedium(fileRelativePath) + MaterialTheme.spacing.VerticalLevel4() + CodeBlock(Modifier.fillMaxWidth(), highlightedSource) + } +} + +private +fun trimIndentationWhitespaces(annotatedString: AnnotatedString): AnnotatedString { + val lines = annotatedString.lines() + val indentation = lines.filter { it.isNotBlank() }.minOf { it.takeWhile(Char::isWhitespace).length } + return buildAnnotatedString { + var nextLineStart = 0 + for ((index, line) in lines.withIndex()) { + val lineStartToTake = nextLineStart + if (line.isBlank()) 0 else indentation + val endIndex = nextLineStart + line.length + if (index != lines.lastIndex) 1 else 0 + val annotatedLine = annotatedString.subSequence(lineStartToTake, endIndex) + append(annotatedLine) + nextLineStart = endIndex + } + } +} diff --git a/gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/connected/actions/GetDeclarativeDocuments.kt b/gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/connected/actions/GetDeclarativeDocuments.kt index 539f456..33b70d2 100644 --- a/gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/connected/actions/GetDeclarativeDocuments.kt +++ b/gradle-client/src/jvmMain/kotlin/org/gradle/client/ui/connected/actions/GetDeclarativeDocuments.kt @@ -12,21 +12,13 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.PointerIcon import androidx.compose.ui.input.pointer.pointerHoverIcon -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.times import org.gradle.client.build.action.GetResolvedDomAction import org.gradle.client.build.model.ResolvedDomPrerequisites -import org.gradle.client.core.gradle.dcl.MutationUtils -import org.gradle.client.core.gradle.dcl.analyzer -import org.gradle.client.core.gradle.dcl.sourceIdentifier -import org.gradle.client.core.gradle.dcl.type +import org.gradle.client.core.gradle.dcl.* import org.gradle.client.ui.build.BuildTextField import org.gradle.client.ui.composables.* import org.gradle.client.ui.connected.TwoPanes @@ -149,9 +141,17 @@ class GetDeclarativeDocuments : GetModelAction.GetCompositeModelAction, - highlightedSourceRangeByFileId: MutableState> - ) { - Column { - val sourceFileData by derivedStateOf { - sources.map { (identifier, content) -> - val highlightedRangeOrNull = highlightedSourceRangeByFileId.value[identifier] - SourceFileData(identifier, sourceFileAnnotatedString(highlightedRangeOrNull, content)) - } - } - - sourceFileData.forEach { data -> - SourceFileTitleAndText(data.relativePath, data.annotatedSource) - MaterialTheme.spacing.VerticalLevel4() - } - } - } - - private data class SourceFileData( - val relativePath: String, - val annotatedSource: AnnotatedString - ) - - private fun sourceFileAnnotatedString( - highlightedSourceRange: IntRange?, - fileContent: String - ) = buildAnnotatedString { - val range = highlightedSourceRange - when { - range == null -> append(fileContent) - - else -> { - append(fileContent.substring(0, range.first)) - withStyle(style = SpanStyle(background = Color.Yellow)) { - append(fileContent.substring(range)) - } - append(fileContent.substring(range.last + 1)) - } - } - } - - @Composable - private fun SourceFileTitleAndText( - fileRelativePath: String, - highlightedSource: AnnotatedString - ) { - TitleMedium(fileRelativePath) - MaterialTheme.spacing.VerticalLevel4() - CodeBlock(Modifier.fillMaxWidth(), highlightedSource) - } - @Composable @OptIn(ExperimentalMaterial3Api::class) private fun DeclarativeFileDropDown( @@ -609,11 +556,6 @@ private data class HighlightingContext( val highlightedSourceRange: MutableState> ) -private data class SourceFileViewInput( - val fileIdentifier: String, - val fileContent: String -) - private fun Modifier.withHoverCursor() = pointerHoverIcon(PointerIcon(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)))