diff --git a/.mise/tasks/list b/.mise/tasks/list index becae19..5ccfef9 100755 --- a/.mise/tasks/list +++ b/.mise/tasks/list @@ -67,13 +67,19 @@ elif not rows: else: print("No notes found.") else: + def _sanitize(val: str) -> str: + return str(val).replace("\t", " ").replace("\n", "⏎") + table = "Title\tType\tStatus\tTags\tUpdated\n" + "\n".join( - f"{row['title']}\t{row['type']}\t{row['status']}\t{', '.join(row['tags'])}\t{row['date']}" for row in rows - ) - subprocess.run( - ["gum", "table", "--print", "--separator", "\t"], - input=table, - text=True, - check=True, + f"{_sanitize(row['title'])}\t{_sanitize(row['type'])}\t{_sanitize(row['status'])}\t{_sanitize(', '.join(row['tags']))}\t{_sanitize(row['date'])}" for row in rows ) + try: + subprocess.run( + ["gum", "table", "--print", "--separator", "\t"], + input=table, + text=True, + check=True, + ) + except (subprocess.CalledProcessError, FileNotFoundError): + print(table) PY diff --git a/.mise/tasks/search b/.mise/tasks/search index eb4b2ac..9521344 100755 --- a/.mise/tasks/search +++ b/.mise/tasks/search @@ -94,13 +94,19 @@ elif not rows: suffix = f" with {' '.join(filters)}" if filters else "" print(f"No notes found matching: {query}{suffix}") else: + def _sanitize(val: str) -> str: + return str(val).replace("\t", " ").replace("\n", "⏎") + table = "Title\tType\tStatus\tUpdated\tMatches\n" + "\n".join( - f"{row['title']}\t{row['type']}\t{row['status']}\t{row['date']}\t{' | '.join(row['matches'][:2])}" for row in rows - ) - subprocess.run( - ["gum", "table", "--print", "--separator", "\t"], - input=table, - text=True, - check=True, + f"{_sanitize(row['title'])}\t{_sanitize(row['type'])}\t{_sanitize(row['status'])}\t{_sanitize(row['date'])}\t{_sanitize(' | '.join(row['matches'][:2]))}" for row in rows ) + try: + subprocess.run( + ["gum", "table", "--print", "--separator", "\t"], + input=table, + text=True, + check=True, + ) + except (subprocess.CalledProcessError, FileNotFoundError): + print(table) PY diff --git a/test/search.bats b/test/search.bats index 3190a17..053a320 100755 --- a/test/search.bats +++ b/test/search.bats @@ -84,3 +84,33 @@ EOF [ "$status" -eq 0 ] [[ "$output" == *"No notes found matching: absent"* ]] } + +@test "search falls back to TSV when gum table rejects unusual characters" { + # Write a note with a literal tab in the title — this breaks gum table's TSV parser + python3 << 'PYEND' +import os +path = os.path.join(os.environ['NOTES_CALLER_PWD'], 'notes', 'unusual.md') +content = ( + '---\n' + 'title: "Tab\tTitle"\n' + 'type: note\n' + 'status: active\n' + 'tags: [test]\n' + 'created: 2026-01-01\n' + 'updated: 2026-01-02\n' + '---\n' + '\n' + '# Tab\tTitle\n' + 'Body with\ta\ttab\ttoo.\n' +) +with open(path, 'w') as f: + f.write(content) +PYEND + + run notes search Tab + [ "$status" -eq 0 ] + # Should get sanitized output (tabs → spaces), not a crash + echo "$output" | grep -q "Tab Title" + # Body match snippet found (matches[:2] limits display, but first body line is enough) + echo "$output" | grep -q "# Tab Title" +}