Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit 7f447d5

Browse files
committed
Update 1.6.4
1 parent 5b10da3 commit 7f447d5

6 files changed

Lines changed: 92 additions & 114 deletions

File tree

app/build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ android {
1717
applicationId = "com.yangdai.opennote"
1818
minSdk = 29
1919
targetSdk = 35
20-
versionCode = 1630
21-
versionName = "1.6.3"
20+
versionCode = 1640
21+
versionName = "1.6.4"
2222

2323
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2424
vectorDrawables {
@@ -112,6 +112,7 @@ dependencies {
112112
implementation(libs.commonmark.ext.gfm.tables)
113113
implementation(libs.commonmark.ext.heading.anchor)
114114
implementation(libs.commonmark.ext.image.attributes)
115+
implementation(libs.commonmark.ext.yaml.front.matter)
115116
implementation(libs.commonmark)
116117

117118
// Room, for local database

app/src/main/java/com/yangdai/opennote/presentation/component/note/LiteTextVisualTransformation.kt

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,14 @@ class LiteTextVisualTransformation(
3939

4040
override fun filter(string: AnnotatedString): TransformedText {
4141
val text = string.text
42+
val styleRanges = findTagRanges(text)
4243
val propertiesRange = text.getPropertiesRange()
43-
var styleRanges = findTagRanges(text)
44-
if (propertiesRange != null) {
45-
styleRanges = StyleRanges(
46-
codeRanges = styleRanges.codeRanges.filter { !it.overlaps(propertiesRange) },
47-
boldRanges = styleRanges.boldRanges.filter { !it.overlaps(propertiesRange) },
48-
italicRanges = styleRanges.italicRanges.filter { !it.overlaps(propertiesRange) },
49-
boldItalicRanges = styleRanges.boldItalicRanges.filter {
50-
!it.overlaps(propertiesRange)
51-
},
52-
strikethroughRanges = styleRanges.strikethroughRanges.filter {
53-
!it.overlaps(propertiesRange)
54-
},
55-
underlineRanges = styleRanges.underlineRanges.filter { !it.overlaps(propertiesRange) },
56-
highlightRanges = styleRanges.highlightRanges.filter { !it.overlaps(propertiesRange) },
57-
headerRanges = styleRanges.headerRanges.filter { !it.first.overlaps(propertiesRange) },
58-
markerRanges = styleRanges.markerRanges.filter { !it.overlaps(propertiesRange) },
59-
linkRanges = styleRanges.linkRanges.filter { !it.overlaps(propertiesRange) },
60-
fencedCodeBlockInfoRanges = styleRanges.fencedCodeBlockInfoRanges.filter {
61-
!it.overlaps(propertiesRange)
62-
},
63-
codeBlockContentRanges = styleRanges.codeBlockContentRanges.filter {
64-
!it.overlaps(propertiesRange)
65-
}
66-
)
67-
}
44+
6845
val annotatedString = buildAnnotatedString {
6946
// 应用语法内文本样式
7047
applyTextStyles(styleRanges)
7148
// 应用语法符号样式
72-
applySymbols(string, styleRanges)
49+
applySymbols(text, styleRanges)
7350
if (propertiesRange != null) applyPropertiesStyle(propertiesRange)
7451
if (!readMode)
7552
applySearchWordStyle(searchWordRanges, currentSearchWordIndex)
@@ -170,7 +147,7 @@ class LiteTextVisualTransformation(
170147
}
171148

172149
private fun AnnotatedString.Builder.applySymbols(
173-
text: AnnotatedString, ranges: StyleRanges
150+
text: String, ranges: StyleRanges
174151
) {
175152
if (selection.start < 0 || selection.end < 0 || readMode) {
176153
applySymbolsCommon(ranges, null)

app/src/main/java/com/yangdai/opennote/presentation/util/LiteVisualHelper.kt

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import com.yangdai.opennote.presentation.theme.linkColor
1818
import com.yangdai.opennote.presentation.util.extension.highlight.Highlight
1919
import com.yangdai.opennote.presentation.util.extension.highlight.HighlightExtension
2020
import com.yangdai.opennote.presentation.util.extension.properties.Properties.splitPropertiesAndContent
21+
import org.commonmark.ext.front.matter.YamlFrontMatterExtension
2122
import org.commonmark.ext.gfm.strikethrough.Strikethrough
2223
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension
2324
import org.commonmark.ext.gfm.tables.TableRow
@@ -48,7 +49,8 @@ val PARSER: Parser =
4849
StrikethroughExtension.create(),
4950
InsExtension.create(),
5051
HighlightExtension.create(),
51-
TablesExtension.create()
52+
TablesExtension.create(),
53+
YamlFrontMatterExtension.create()
5254
)
5355
).includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build()
5456

@@ -413,26 +415,24 @@ fun findTagRanges(text: String): StyleRanges {
413415
}
414416

415417
override fun visit(fencedCodeBlock: FencedCodeBlock) {
416-
val span = fencedCodeBlock.sourceSpans.firstOrNull()
417-
if (span != null) {
418-
// Get the opening fence marker (```language)
419-
val openingFence = span.inputIndex
420-
val infoString = fencedCodeBlock.info?.length ?: 0
421-
val openingMarkerLength = fencedCodeBlock.openingFenceLength ?: return
422-
423-
markerRanges.add(openingFence until (openingFence + openingMarkerLength)) // ```
424-
fencedCodeBlockInfoRanges.add(openingFence + openingMarkerLength until (openingFence + openingMarkerLength + infoString)) // language
425-
426-
val blockContentLength =
427-
if (fencedCodeBlock.literal.isEmpty()) 0 else fencedCodeBlock.literal.length + 1
428-
val fence =
429-
openingFence + openingMarkerLength + infoString + blockContentLength
430-
codeBlockContentRanges.add((openingFence + openingMarkerLength + infoString) until fence) // content
431-
432-
val closingMarkerLength = fencedCodeBlock.closingFenceLength ?: return
433-
if (fence + closingMarkerLength <= text.length) {
434-
markerRanges.add(fence until (fence + closingMarkerLength))
435-
}
418+
val span = fencedCodeBlock.sourceSpans.firstOrNull() ?: return
419+
420+
// Get the opening fence marker (```language)
421+
val openingFenceStartIndex = span.inputIndex
422+
val openingMarkerLength = fencedCodeBlock.openingFenceLength ?: return
423+
val infoStringStartIndex = openingFenceStartIndex + openingMarkerLength
424+
markerRanges.add(openingFenceStartIndex until infoStringStartIndex) // ```
425+
val infoStringLength = fencedCodeBlock.info?.length ?: 0
426+
fencedCodeBlockInfoRanges.add(infoStringStartIndex until (infoStringStartIndex + infoStringLength)) // language
427+
428+
val closingMarkerLength = fencedCodeBlock.closingFenceLength ?: return
429+
val blockContentLength =
430+
if (fencedCodeBlock.literal.isEmpty()) 0 else fencedCodeBlock.literal.length + 1
431+
val fence =
432+
openingFenceStartIndex + openingMarkerLength + infoStringLength + blockContentLength
433+
codeBlockContentRanges.add((openingFenceStartIndex + openingMarkerLength + infoStringLength) until fence) // content
434+
if (fence + closingMarkerLength <= text.length) {
435+
markerRanges.add(fence until (fence + closingMarkerLength))
436436
}
437437
}
438438

@@ -470,31 +470,42 @@ fun parseMarkdownContent(text: String): AnnotatedString {
470470
val styleRanges = findTagRanges(textWithoutProperties)
471471

472472
return buildAnnotatedString {
473+
fun safeAddStyle(style: SpanStyle, start: Int, end: Int) {
474+
val safeStart = start.coerceAtLeast(0).coerceAtMost(text.length)
475+
val safeEnd = end.coerceAtLeast(0).coerceAtMost(text.length)
476+
addStyle(style, safeStart, safeEnd)
477+
}
478+
479+
fun safeAddStyle(style: ParagraphStyle, start: Int, end: Int) {
480+
val safeStart = start.coerceAtLeast(0).coerceAtMost(text.length)
481+
val safeEnd = end.coerceAtLeast(0).coerceAtMost(text.length)
482+
addStyle(style, safeStart, safeEnd)
483+
}
473484
styleRanges.apply {
474485
codeRanges.forEach { range ->
475-
addStyle(CODE_STYLE, range.first, range.last + 1)
476-
addStyle(SYMBOL_STYLE, range.first, range.first + 1)
477-
addStyle(SYMBOL_STYLE, range.last - 1 + 1, range.last + 1)
486+
safeAddStyle(CODE_STYLE, range.first, range.last + 1)
487+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + 1)
488+
safeAddStyle(SYMBOL_STYLE, range.last - 1 + 1, range.last + 1)
478489
}
479490
boldItalicRanges.forEach { range ->
480-
addStyle(BOLD_ITALIC_STYLE, range.first, range.last + 1)
481-
addStyle(SYMBOL_STYLE, range.first, range.first + 3)
482-
addStyle(SYMBOL_STYLE, range.last - 3 + 1, range.last + 1)
491+
safeAddStyle(BOLD_ITALIC_STYLE, range.first, range.last + 1)
492+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + 3)
493+
safeAddStyle(SYMBOL_STYLE, range.last - 3 + 1, range.last + 1)
483494
}
484495
boldRanges.forEach { range ->
485-
addStyle(BOLD_STYLE, range.first, range.last + 1)
486-
addStyle(SYMBOL_STYLE, range.first, range.first + 2)
487-
addStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
496+
safeAddStyle(BOLD_STYLE, range.first, range.last + 1)
497+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + 2)
498+
safeAddStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
488499
}
489500
italicRanges.forEach { range ->
490-
addStyle(ITALIC_STYLE, range.first, range.last + 1)
491-
addStyle(SYMBOL_STYLE, range.first, range.first + 1)
492-
addStyle(SYMBOL_STYLE, range.last - 1 + 1, range.last + 1)
501+
safeAddStyle(ITALIC_STYLE, range.first, range.last + 1)
502+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + 1)
503+
safeAddStyle(SYMBOL_STYLE, range.last - 1 + 1, range.last + 1)
493504
}
494505
highlightRanges.forEach { range ->
495-
addStyle(HIGHLIGHT_STYLE, range.first, range.last + 1)
496-
addStyle(SYMBOL_STYLE, range.first, range.first + 2)
497-
addStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
506+
safeAddStyle(HIGHLIGHT_STYLE, range.first, range.last + 1)
507+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + 2)
508+
safeAddStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
498509
}
499510

500511
val combinedRanges = (strikethroughRanges + underlineRanges).distinct()
@@ -507,36 +518,36 @@ fun parseMarkdownContent(text: String): AnnotatedString {
507518
hasUnderline -> UNDERLINE_STYLE
508519
else -> return@forEach
509520
}
510-
addStyle(style, range.first, range.last + 1)
521+
safeAddStyle(style, range.first, range.last + 1)
511522
}
512523

513524
strikethroughRanges.forEach { range ->
514-
addStyle(SYMBOL_STYLE, range.first, range.first + 2)
515-
addStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
525+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + 2)
526+
safeAddStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
516527
}
517528
underlineRanges.forEach { range ->
518-
addStyle(SYMBOL_STYLE, range.first, range.first + 2)
519-
addStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
529+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + 2)
530+
safeAddStyle(SYMBOL_STYLE, range.last - 2 + 1, range.last + 1)
520531
}
521532

522533
headerRanges.forEach { (range, level) ->
523-
addStyle(HEADER_STYLES[level - 1], range.first, range.last + 1)
524-
addStyle(HEADER_LINE_STYLES[level - 1], range.first, range.last + 1)
525-
addStyle(SYMBOL_STYLE, range.first, range.first + level + 1)
534+
safeAddStyle(HEADER_STYLES[level - 1], range.first, range.last + 1)
535+
safeAddStyle(HEADER_LINE_STYLES[level - 1], range.first, range.last + 1)
536+
safeAddStyle(SYMBOL_STYLE, range.first, range.first + level + 1)
526537
}
527538

528539
// Add styling for list markers
529540
markerRanges.forEach { range ->
530-
addStyle(MARKER_STYLE, range.first, range.last + 1)
541+
safeAddStyle(MARKER_STYLE, range.first, range.last + 1)
531542
}
532543
linkRanges.forEach { range ->
533-
addStyle(LINK_STYLE, range.first, range.last + 1)
544+
safeAddStyle(LINK_STYLE, range.first, range.last + 1)
534545
}
535546
fencedCodeBlockInfoRanges.forEach { range ->
536-
addStyle(KEYWORD_STYLE, range.first, range.last + 1)
547+
safeAddStyle(KEYWORD_STYLE, range.first, range.last + 1)
537548
}
538549
codeBlockContentRanges.forEach { range ->
539-
addStyle(CODE_BLOCK_STYLE, range.first, range.last + 1)
550+
safeAddStyle(CODE_BLOCK_STYLE, range.first, range.last + 1)
540551
}
541552
}
542553
append(textWithoutProperties)

app/src/main/java/com/yangdai/opennote/presentation/viewmodel/FileViewModel.kt

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ import com.yangdai.opennote.presentation.state.TextState
4242
import com.yangdai.opennote.presentation.util.Constants
4343
import com.yangdai.opennote.presentation.util.PARSER
4444
import com.yangdai.opennote.presentation.util.extension.highlight.HighlightExtension
45-
import com.yangdai.opennote.presentation.util.extension.properties.Properties.getPropertiesRange
46-
import com.yangdai.opennote.presentation.util.extension.properties.Properties.splitPropertiesAndContent
4745
import com.yangdai.opennote.presentation.util.getFileName
4846
import com.yangdai.opennote.presentation.util.getOrCreateDirectory
4947
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -67,6 +65,7 @@ import kotlinx.serialization.json.Json
6765
import org.commonmark.Extension
6866
import org.commonmark.ext.autolink.AutolinkExtension
6967
import org.commonmark.ext.footnotes.FootnotesExtension
68+
import org.commonmark.ext.front.matter.YamlFrontMatterExtension
7069
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension
7170
import org.commonmark.ext.gfm.tables.TablesExtension
7271
import org.commonmark.ext.heading.anchor.HeadingAnchorExtension
@@ -107,7 +106,8 @@ class FileViewModel @Inject constructor(
107106
ImageAttributesExtension.create(),
108107
StrikethroughExtension.create(),
109108
TaskListItemsExtension.create(),
110-
HighlightExtension.create()
109+
HighlightExtension.create(),
110+
YamlFrontMatterExtension.create()
111111
)
112112
private val parser = Parser.builder().extensions(extensions).build()
113113
private val renderer = HtmlRenderer.builder().extensions(extensions).build()
@@ -121,9 +121,7 @@ class FileViewModel @Inject constructor(
121121
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
122122
val html = contentSnapshotFlow.debounce(100)
123123
.mapLatest {
124-
var content = it.toString()
125-
content = content.splitPropertiesAndContent().second
126-
renderer.render(parser.parse(content))
124+
renderer.render(parser.parse(it.toString()))
127125
}
128126
.flowOn(Dispatchers.Default)
129127
.stateIn(
@@ -145,27 +143,23 @@ class FileViewModel @Inject constructor(
145143
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
146144
val outline = contentSnapshotFlow.debounce(1000)
147145
.mapLatest {
148-
val content = it.toString()
149-
val propertiesRange = content.getPropertiesRange()
150-
val document = PARSER.parse(content)
146+
val document = PARSER.parse(it.toString())
151147
val root = HeaderNode("", 0, IntRange.EMPTY)
152148
val headerStack = mutableListOf(root)
153149
document.accept(object : AbstractVisitor() {
154150
override fun visit(heading: Heading) {
155151
val span = heading.sourceSpans.first()
156152
val range = span.inputIndex until (span.inputIndex + span.length)
157153

158-
// Skip headings that are inside the properties section
159-
if (propertiesRange == null || !propertiesRange.contains(range.first)) {
160-
val title = it.substring(range).replace("#", "").trim()
161-
val node = HeaderNode(title, heading.level, range)
154+
val title = it.substring(range).replace("#", "").trim()
155+
val node = HeaderNode(title, heading.level, range)
162156

163-
while (headerStack.last().level >= heading.level) {
164-
headerStack.removeAt(headerStack.lastIndex)
165-
}
166-
headerStack.last().children.add(node)
167-
headerStack.add(node)
157+
while (headerStack.last().level >= heading.level) {
158+
headerStack.removeAt(headerStack.lastIndex)
168159
}
160+
headerStack.last().children.add(node)
161+
headerStack.add(node)
162+
169163
visitChildren(heading)
170164
}
171165
})

0 commit comments

Comments
 (0)