better tag search

This commit is contained in:
randogoth 2025-02-22 15:58:17 +02:00
parent f77a66d5d8
commit 004d2d9e82

View file

@ -194,45 +194,74 @@ class _NoteListScreenState extends State<NoteListScreen> {
} }
void _filterNotes() { void _filterNotes() {
final query = searchController.text.trim().toLowerCase(); final query = searchController.text.trim();
if (query.isEmpty) {
setState(() {
filteredNotes = notes;
});
return;
}
final List<String> words = query.split(RegExp(r'\s+')); // Split by spaces
final List<String> terms = [];
final List<String> tags = [];
for (final word in words) {
if (word.startsWith('#') || word.startsWith('+') || word.startsWith('@')) {
tags.add(word.toLowerCase());
} else {
terms.add(word.toLowerCase());
}
}
setState(() { setState(() {
if (query.startsWith('/')) { filteredNotes = notes.where((note) {
// Extract subfolder path and search term final content = note.readAsStringSync().toLowerCase();
final parts = query.split('/'); final filename = note.uri.pathSegments.last.toLowerCase();
final subfolderPath = parts.sublist(1, parts.length - 1).join('/');
final searchTerm = parts.last;
filteredNotes = notes.where((note) { // Check if all terms are found in filename or content
final relativePath = path.relative(note.path, from: notesDirectoryPath!); final bool matchesTerms = terms.isEmpty ||
return relativePath.startsWith(subfolderPath) && terms.every((term) => filename.contains(term) || content.contains(term));
(relativePath.contains(searchTerm) || note.readAsStringSync().toLowerCase().contains(searchTerm));
}).toList(); // Check if all tags are found in content
} else { final bool matchesTags = tags.isEmpty ||
// Default search across all notes tags.every((tag) => content.contains(tag));
filteredNotes = notes.where((note) {
final content = note.readAsStringSync().toLowerCase(); return matchesTerms && matchesTags;
return note.uri.pathSegments.last.toLowerCase().contains(query) || content.contains(query); }).toList();
}).toList();
}
}); });
} }
List<InlineSpan> _formatTags(String content) { List<InlineSpan> _formatTags(String content) {
final tagPattern = RegExp(r'([#@+][a-zA-Z0-9_]+)'); final tagPattern = RegExp(r'([#@+][a-zA-Z0-9_]+)');
final List<InlineSpan> spans = []; final Set<String> tagSet = {}; // Avoid duplicate tags
for (final match in tagPattern.allMatches(content)) { for (final match in tagPattern.allMatches(content)) {
final tag = match.group(0)!; tagSet.add(match.group(0)!);
}
// Sort tags: +tags before @tags before #tags
final sortedTags = tagSet.toList()
..sort((a, b) {
if (a.startsWith('+') && !b.startsWith('+')) return -1;
if (b.startsWith('+') && !a.startsWith('+')) return 1;
if (a.startsWith('@') && !b.startsWith('@')) return -1;
if (b.startsWith('@') && !a.startsWith('@')) return 1;
return a.compareTo(b); // Default alphabetical order
});
return sortedTags.map((tag) {
Color tagColor = Colors.white; Color tagColor = Colors.white;
if (tag.startsWith('#')) tagColor = Colors.pinkAccent; if (tag.startsWith('#')) tagColor = Colors.pinkAccent;
if (tag.startsWith('+')) tagColor = Colors.cyan; if (tag.startsWith('+')) tagColor = Colors.cyan;
if (tag.startsWith('@')) tagColor = Colors.orange; if (tag.startsWith('@')) tagColor = Colors.orange;
spans.add(TextSpan( return TextSpan(
text: tag + ' ', text: "$tag ",
style: TextStyle(color: tagColor, fontWeight: FontWeight.bold))); style: TextStyle(color: tagColor, fontWeight: FontWeight.bold),
} );
return spans; }).toList();
} }
void _createNewNote(String title) async { void _createNewNote(String title) async {
@ -591,23 +620,34 @@ class _NoteListScreenState extends State<NoteListScreen> {
child: ListTile( child: ListTile(
title: title:
Text(noteFile.uri.pathSegments.last.replaceAll('.md', '')), Text(noteFile.uri.pathSegments.last.replaceAll('.md', '')),
subtitle: Row( subtitle: LayoutBuilder(
children: [ builder: (context, constraints) {
path.relative(path.dirname(noteFile.path), from: notesDirectoryPath!) != "." final bool hasEnoughSpace = constraints.maxWidth > 200; // Adjust as needed
? Text(
path.relative(path.dirname(noteFile.path), from: notesDirectoryPath!), return Row(
style: TextStyle(color: Colors.blueGrey), children: [
) if (path.relative(path.dirname(noteFile.path), from: notesDirectoryPath!) != ".")
: Text(""), Padding(
widget.showTags && tagSpans.isNotEmpty padding: EdgeInsets.only(right: 8),
? RichText( child: Text(
text: TextSpan( path.relative(path.dirname(noteFile.path), from: notesDirectoryPath!),
style: DefaultTextStyle.of(context).style, style: TextStyle(color: Colors.blueGrey),
children: tagSpans) ),
) ),
: Text("") if (widget.showTags && hasEnoughSpace && tagSpans.isNotEmpty)
], Expanded(
), child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: tagSpans,
),
overflow: TextOverflow.ellipsis, // Prevents overflow errors
),
),
],
);
},
),
onTap: () async { onTap: () async {
if (_useExternalEditor) { if (_useExternalEditor) {
// Open with system's default editor // Open with system's default editor