Skip to content
Merged
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 @@ -7,8 +7,10 @@ package io.github.maxdac.marknotes.core.notes
* The apps that sync notes compute the same key in the shared sync library (`foldCase`); both must agree, because
* a rename is "case only" exactly when the keys are equal. This version has no dependency on that library and
* folds through the JVM's simple case mappings (upper, then lower), which gives the same equivalence classes as the
* `C` and `S` mappings of CaseFolding.txt except for the dotted and dotless I, kept apart below. The sync
* library's own test vectors are repeated in `CaseFoldingTest`.
* `C` and `S` mappings of CaseFolding.txt except for the dotted and dotless I, kept apart below, and the three
* `S` mappings of Unicode 15.1 that older JVMs lack, added below. The keys themselves may differ from the library's
* (it folds Cherokee to upper case): only the classes must agree. The sync library's own test vectors are repeated
* in `CaseFoldingTest`.
*/
fun foldCase(path: String): String {
val folded = StringBuilder(path.length)
Expand All @@ -18,6 +20,14 @@ fun foldCase(path: String): String {
index += Character.charCount(codePoint)
val key = when (codePoint) {
DOTTED_CAPITAL_I, DOTLESS_SMALL_I -> codePoint

// Simple foldings added in Unicode 15.1 that the JVM's upper-then-lower mapping lacks.
GREEK_SMALL_IOTA_DIALYTIKA_PERISPOMENI -> GREEK_SMALL_IOTA_DIALYTIKA_TONOS

GREEK_SMALL_UPSILON_DIALYTIKA_PERISPOMENI -> GREEK_SMALL_UPSILON_DIALYTIKA_TONOS

LONG_S_T_LIGATURE -> ST_LIGATURE

else -> Character.toLowerCase(Character.toUpperCase(codePoint))
}
folded.appendCodePoint(key)
Expand All @@ -27,3 +37,9 @@ fun foldCase(path: String): String {

private const val DOTTED_CAPITAL_I = 0x0130
private const val DOTLESS_SMALL_I = 0x0131
private const val GREEK_SMALL_IOTA_DIALYTIKA_PERISPOMENI = 0x1FD3
private const val GREEK_SMALL_IOTA_DIALYTIKA_TONOS = 0x0390
private const val GREEK_SMALL_UPSILON_DIALYTIKA_PERISPOMENI = 0x1FE3
private const val GREEK_SMALL_UPSILON_DIALYTIKA_TONOS = 0x03B0
private const val LONG_S_T_LIGATURE = 0xFB05
private const val ST_LIGATURE = 0xFB06
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ internal fun fileIdentity(file: File): String =
Files.readAttributes(file.toPath(), BasicFileAttributes::class.java).fileKey()?.toString()
?: if (windows) WindowsFiles.identity(file) else throw IOException("Cannot verify file identity: $file")

/** Never use Java moves here: Unix implementations may check existence then perform a replacing rename. */
internal fun renameCaseFile(source: File, target: File) {
/**
* Renames [source] to [target] without ever replacing an existing file, then checks that the exact case of the new
* name and the file identity survived. Never use Java moves here: Unix implementations may check existence then
* perform a replacing rename. Public for apps that keep their own files next to the notes (the sync base copies).
*/
fun renameCaseFile(source: File, target: File) {
val identity = fileIdentity(source)
moveFileNoReplace(source, target)
val names = target.parentFile.list().orEmpty().toList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.nio.file.attribute.FileTime

internal enum class NewFileStage { BYTES_WRITTEN, MTIME_SET, FLUSHED, PUBLISHED }
/** The steps of [writeNewFile], reported to its `checkpoint` so tests can interrupt it after each one. */
enum class NewFileStage { BYTES_WRITTEN, MTIME_SET, FLUSHED, PUBLISHED }

/** Only complete, timestamped, flushed bytes may become visible at the create-only destination. */
internal fun writeNewFile(
fun writeNewFile(
target: File,
bytes: ByteArray,
modifiedMillis: Long,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ class CaseFoldingTest {
assertEquals(foldCase("ſ/Σ.md"), foldCase("S/ς.md"))
}

@Test
fun foldingsAddedInUnicode151JoinTheirTargets() {
// The JVM's upper-then-lower mapping lacks these; the shared sync library folds them.
assertEquals(foldCase("\u0390.md"), foldCase("\u1FD3.md"))
assertEquals(foldCase("\u03B0.md"), foldCase("\u1FE3.md"))
assertEquals(foldCase("\uFB06.md"), foldCase("\uFB05.md"))
}

@Test
fun dottedAndDotlessIAreNotFoldedToI() {
assertNotEquals(foldCase("İ.md"), foldCase("i.md"))
Expand Down
Loading