From c4960e8ee75ce8f31fc1e3b6d79d95ed45ade129 Mon Sep 17 00:00:00 2001 From: Test Date: Fri, 27 Mar 2026 07:10:06 +0100 Subject: [PATCH] =?UTF-8?q?refactor:=20reduce=20complexity=20in=20parsing.?= =?UTF-8?q?rs=20and=20noteListHelpers.ts=20(gate:=209.31=20=E2=86=92=209.3?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored two files to improve code health: 1. src-tauri/src/vault/parsing.rs (7.9 → 8.54, +0.64) - Extracted helper functions to reduce nesting in strip_markdown_chars (process_wikilink, extract_wikilink_display, process_markdown_link) - Refactored strip_list_marker to eliminate bumpy road pattern (strip_unordered_marker, strip_ordered_marker) 2. src/utils/noteListHelpers.ts (9.28 → 10.0, +0.72) - Reduced cyclomatic complexity in isInboxEntry from 15 to ~4 (hasAnyValidLinks, hasValidBodyLinks, hasValidFrontmatterLinks) - Extracted complex conditional into wasCreatedBeforeLastModification Gate threshold raised: 9.31 → 9.33 (conservative, pending CodeScene re-analysis) Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci.yml | 2 +- .husky/pre-push | 12 +- src-tauri/Cargo.lock | 285 +++++++++++++++++++++++++++++++++ src-tauri/src/vault/parsing.rs | 75 ++++++--- src/utils/noteListHelpers.ts | 32 ++-- 5 files changed, 364 insertions(+), 42 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 557fb429..7591ca11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,7 +91,7 @@ jobs: CODESCENE_PROJECT_ID: ${{ secrets.CODESCENE_PROJECT_ID }} run: | HOTSPOT_THRESHOLD=9.5 - AVERAGE_THRESHOLD=9.31 + AVERAGE_THRESHOLD=9.33 API_RESPONSE=$(curl -sf \ -H "Authorization: Bearer $CODESCENE_PAT" \ -H "Accept: application/json" \ diff --git a/.husky/pre-push b/.husky/pre-push index 3fac9d0c..d81eb66c 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -111,11 +111,11 @@ else fi # ── 5. CodeScene code health gate ──────────────────────────────────────── -# Blocks push if Hotspot < 9.5 OR Average < 9.0. +# Blocks push if Hotspot < 9.5 OR Average < 9.33. # Remote scores reflect state after last push — this catches regressions # introduced in a previous push that weren't caught yet. echo "" -echo "🏥 [5/5] CodeScene code health (Hotspot ≥9.5 + Average ≥9.0)..." +echo "🏥 [5/5] CodeScene code health (Hotspot ≥9.5 + Average ≥9.33)..." if [ -z "$CODESCENE_PAT" ] || [ -z "$CODESCENE_PROJECT_ID" ]; then echo " ⚠️ CODESCENE_PAT or CODESCENE_PROJECT_ID not set — skipping" else @@ -129,7 +129,7 @@ else echo " ⚠️ Could not fetch remote scores — skipping (CI will enforce)" else echo " Remote Hotspot Code Health: $HOTSPOT_SCORE (threshold: 9.5)" - echo " Remote Average Code Health: $AVERAGE_SCORE (threshold: 8.9)" + echo " Remote Average Code Health: $AVERAGE_SCORE (threshold: 9.33)" python3 -c " import sys hotspot = float('$HOTSPOT_SCORE') @@ -140,11 +140,11 @@ if hotspot < 9.5: failed = True else: print(f'OK: Hotspot {hotspot:.2f} >= 9.5') -if average < 9.31: - print(f'FAIL: Average Code Health {average:.2f} < 9.31 — regressions detected, fix before pushing') +if average < 9.33: + print(f'FAIL: Average Code Health {average:.2f} < 9.33 — regressions detected, fix before pushing') failed = True else: - print(f'OK: Average {average:.2f} >= 9.31') + print(f'OK: Average {average:.2f} >= 9.33') if failed: sys.exit(1) " || exit 1 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 05b83bde..5319764e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.1" @@ -290,6 +299,21 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + [[package]] name = "base64" version = "0.21.7" @@ -804,6 +828,16 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + [[package]] name = "deranged" version = "0.5.6" @@ -1143,6 +1177,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "flate2" version = "1.1.9" @@ -1239,6 +1285,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1482,6 +1529,12 @@ dependencies = [ "wasip3", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "gio" version = "0.18.4" @@ -1728,6 +1781,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hostname" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +dependencies = [ + "cfg-if", + "libc", + "windows-link 0.2.1", +] + [[package]] name = "html5ever" version = "0.29.1" @@ -2205,6 +2269,7 @@ dependencies = [ "mockito", "regex", "reqwest 0.12.28", + "sentry", "serde", "serde_json", "tauri", @@ -2216,6 +2281,7 @@ dependencies = [ "tauri-plugin-updater", "tempfile", "tokio", + "uuid", "walkdir", ] @@ -2496,6 +2562,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -2635,6 +2713,16 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-text" version = "0.3.2" @@ -2751,8 +2839,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.11.0", + "block2", "objc2", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", "objc2-foundation", ] @@ -2772,6 +2879,15 @@ dependencies = [ "objc2-security", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -2850,6 +2966,22 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + [[package]] name = "osakit" version = "0.3.1" @@ -3514,6 +3646,7 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", @@ -3670,6 +3803,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3909,6 +4048,114 @@ dependencies = [ "serde_core", ] +[[package]] +name = "sentry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255914a8e53822abd946e2ce8baa41d4cded6b8e938913b7f7b9da5b7ab44335" +dependencies = [ + "httpdate", + "native-tls", + "reqwest 0.12.28", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "tokio", + "ureq", +] + +[[package]] +name = "sentry-backtrace" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00293cd332a859961f24fd69258f7e92af736feaeb91020cff84dac4188a4302" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "961990f9caa76476c481de130ada05614cd7f5aa70fb57c2142f0e09ad3fb2aa" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a6409d845707d82415c800290a5d63be5e3df3c2e417b0997c60531dfbd35ef" +dependencies = [ + "once_cell", + "rand 0.8.5", + "sentry-types", + "serde", + "serde_json", +] + +[[package]] +name = "sentry-debug-images" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ab5df4f3b64760508edfe0ba4290feab5acbbda7566a79d72673065888e5cc" +dependencies = [ + "findshlibs", + "once_cell", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609b1a12340495ce17baeec9e08ff8ed423c337c1a84dffae36a178c783623f3" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tracing" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f4e86402d5c50239dc7d8fd3f6d5e048221d5fcb4e026d8d50ab57fe4644cb" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3f117b8755dbede8260952de2aeb029e20f432e72634e8969af34324591631" +dependencies = [ + "debugid", + "hex", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror 1.0.69", + "time", + "url", + "uuid", +] + [[package]] name = "serde" version = "1.0.228" @@ -5131,6 +5378,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "tracing-core", ] [[package]] @@ -5184,6 +5441,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -5249,6 +5515,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "log", + "native-tls", + "once_cell", + "url", +] + [[package]] name = "url" version = "2.5.8" @@ -5304,6 +5583,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "value-bag" version = "1.12.0" diff --git a/src-tauri/src/vault/parsing.rs b/src-tauri/src/vault/parsing.rs index 914c3626..5062045b 100644 --- a/src-tauri/src/vault/parsing.rs +++ b/src-tauri/src/vault/parsing.rs @@ -73,19 +73,26 @@ fn extract_subheading_text(line: &str) -> Option<&str> { /// Strip leading list markers (*, -, +, 1.) from a line. fn strip_list_marker(line: &str) -> &str { let t = line.trim_start(); - // Unordered: "* ", "- ", "+ " - for prefix in &["* ", "- ", "+ "] { - if let Some(rest) = t.strip_prefix(prefix) { - return rest; - } + strip_unordered_marker(t) + .or_else(|| strip_ordered_marker(t)) + .unwrap_or(t) +} + +/// Strip unordered list markers: "* ", "- ", "+ " +fn strip_unordered_marker(s: &str) -> Option<&str> { + ["* ", "- ", "+ "] + .iter() + .find_map(|prefix| s.strip_prefix(prefix)) +} + +/// Strip ordered list markers: "1. ", "2. ", etc. +fn strip_ordered_marker(s: &str) -> Option<&str> { + let dot_pos = s.find(". ")?; + if dot_pos <= 3 && s[..dot_pos].chars().all(|c| c.is_ascii_digit()) { + Some(&s[dot_pos + 2..]) + } else { + None } - // Ordered: "1. ", "2. ", etc. - if let Some(dot_pos) = t.find(". ") { - if dot_pos <= 3 && t[..dot_pos].chars().all(|c| c.is_ascii_digit()) { - return &t[dot_pos + 2..]; - } - } - t } /// Truncate a string to `max_len` bytes at a valid UTF-8 boundary, appending "...". @@ -188,20 +195,10 @@ fn strip_markdown_chars(s: &str) -> String { while let Some(ch) = chars.next() { match ch { '[' if chars.peek() == Some(&'[') => { - chars.next(); // consume second '[' - let inner = collect_wikilink_inner(&mut chars); - match inner.find('|') { - Some(idx) => result.push_str(&inner[idx + 1..]), - None => result.push_str(&inner), - } + process_wikilink(&mut chars, &mut result); } '[' => { - let inner = collect_until(&mut chars, ']'); - if chars.peek() == Some(&'(') { - chars.next(); - skip_until(&mut chars, ')'); - } - result.push_str(&inner); + process_markdown_link(&mut chars, &mut result); } c if is_markdown_formatting(c) => {} _ => result.push(ch), @@ -210,6 +207,36 @@ fn strip_markdown_chars(s: &str) -> String { result } +/// Process a wikilink `[[...]]` or `[[...|display]]`, extracting the display text. +fn process_wikilink( + chars: &mut std::iter::Peekable>, + result: &mut String, +) { + chars.next(); // consume second '[' + let inner = collect_wikilink_inner(chars); + let display_text = extract_wikilink_display(&inner); + result.push_str(display_text); +} + +/// Extract display text from wikilink inner content. +/// Returns the part after '|' if present, otherwise the whole inner text. +fn extract_wikilink_display(inner: &str) -> &str { + inner.find('|').map_or(inner, |idx| &inner[idx + 1..]) +} + +/// Process a markdown link `[text](url)`, extracting only the link text. +fn process_markdown_link( + chars: &mut std::iter::Peekable>, + result: &mut String, +) { + let inner = collect_until(chars, ']'); + if chars.peek() == Some(&'(') { + chars.next(); + skip_until(chars, ')'); + } + result.push_str(&inner); +} + /// Collect chars inside a wikilink until `]]`, consuming both closing brackets. fn collect_wikilink_inner(chars: &mut std::iter::Peekable>) -> String { let mut buf = String::new(); diff --git a/src/utils/noteListHelpers.ts b/src/utils/noteListHelpers.ts index aeaa8c26..8db9e589 100644 --- a/src/utils/noteListHelpers.ts +++ b/src/utils/noteListHelpers.ts @@ -43,12 +43,16 @@ export function formatSubtitle(entry: VaultEntry): string { return parts.join(' \u00b7 ') } +function wasCreatedBeforeLastModification(entry: VaultEntry): boolean { + return !!(entry.createdAt && entry.modifiedAt && entry.createdAt !== entry.modifiedAt) +} + export function formatSearchSubtitle(entry: VaultEntry): string { const parts: string[] = [] const modified = entry.modifiedAt ?? entry.createdAt if (modified) parts.push(relativeDate(modified)) - if (entry.createdAt && entry.modifiedAt && entry.createdAt !== entry.modifiedAt) { - parts.push(`Created ${relativeDate(entry.createdAt)}`) + if (wasCreatedBeforeLastModification(entry)) { + parts.push(`Created ${relativeDate(entry.createdAt!)}`) } if (entry.wordCount > 0) { parts.push(`${entry.wordCount.toLocaleString()} words`) @@ -391,20 +395,26 @@ function hasValidRef(refs: string[], validTargets: Set): boolean { export function isInboxEntry(entry: VaultEntry, validTargets: Set): boolean { if (entry.trashed || entry.archived) return false if (entry.isA === 'Type') return false + return !hasAnyValidLinks(entry, validTargets) +} - // Check body outgoing links - if (entry.outgoingLinks.some((link) => validTargets.has(link) || validTargets.has(link.split('/').pop() ?? ''))) return false +function hasAnyValidLinks(entry: VaultEntry, validTargets: Set): boolean { + return hasValidBodyLinks(entry.outgoingLinks, validTargets) || hasValidFrontmatterLinks(entry, validTargets) +} + +function hasValidBodyLinks(outgoingLinks: string[], validTargets: Set): boolean { + return outgoingLinks.some((link) => validTargets.has(link) || validTargets.has(link.split('/').pop() ?? '')) +} + +function hasValidFrontmatterLinks(entry: VaultEntry, validTargets: Set): boolean { + if (entry.belongsTo?.length && hasValidRef(entry.belongsTo, validTargets)) return true + if (entry.relatedTo?.length && hasValidRef(entry.relatedTo, validTargets)) return true - // Check frontmatter relationship refs - if (entry.belongsTo?.length && hasValidRef(entry.belongsTo, validTargets)) return false - if (entry.relatedTo?.length && hasValidRef(entry.relatedTo, validTargets)) return false if (entry.relationships) { - for (const refs of Object.values(entry.relationships)) { - if (hasValidRef(refs, validTargets)) return false - } + return Object.values(entry.relationships).some((refs) => hasValidRef(refs, validTargets)) } - return true + return false } const INBOX_PERIOD_DAYS: Record = {