cache note metadata at load time to eliminate per-search file I/O
_filterNotes was calling note.readAsStringSync() and _extractTags() for every note on every keystroke, plus recomputing path.relative() each time. For large collections with many subdirectories this is O(n * disk). Fix: build three maps during _loadNotes (which already reads every file once) and use them in _filterNotes instead of touching the filesystem: - _noteRelativePaths: pre-lowercased relative path per note - _noteContentLower: pre-lowercased content for full-text search - noteTags: already existed, now actually used in filter _filterNotes now does only in-memory map lookups and string.contains checks — no file reads, no regex, no path computation per keystroke. Also fix _createNewNote: the Navigator.push was not awaited so _loadNotes() was never called on return, leaving stale caches after editing a freshly created note. Now awaits the push and reloads on return, mirroring the existing-note tap behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6470a878fd
commit
f91484d6d4
1 changed files with 21 additions and 11 deletions
|
|
@ -40,6 +40,8 @@ class _NoteListScreenState extends State<NoteListScreen> {
|
|||
int _visibleLimit = 30;
|
||||
bool _isLoadingMore = false;
|
||||
Map<File, Set<String>> noteTags = {};
|
||||
Map<File, String> _noteRelativePaths = {};
|
||||
Map<File, String> _noteContentLower = {};
|
||||
String? notesDirectoryPath;
|
||||
String? defaultDir;
|
||||
bool _useExternalEditor = false;
|
||||
|
|
@ -239,7 +241,9 @@ Happy note taking! ✨
|
|||
|
||||
List<File> allNotes = [];
|
||||
Map<String, DateTime> folderTimestamps = {};
|
||||
Map<File, Set<String>> extractedTags = {}; // ✅ Store extracted tags here
|
||||
Map<File, Set<String>> extractedTags = {};
|
||||
Map<File, String> extractedRelPaths = {};
|
||||
Map<File, String> extractedContentLower = {};
|
||||
|
||||
void fetchNotes(Directory dir) {
|
||||
final entries = dir.listSync(recursive: _includeSubdirectories);
|
||||
|
|
@ -261,6 +265,8 @@ Happy note taking! ✨
|
|||
|
||||
final content = entry.readAsStringSync();
|
||||
extractedTags[entry] = _extractTags(content);
|
||||
extractedRelPaths[entry] = path.relative(entry.path, from: notesDirectoryPath!).toLowerCase();
|
||||
extractedContentLower[entry] = content.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +292,9 @@ Happy note taking! ✨
|
|||
setState(() {
|
||||
notes = allNotes;
|
||||
filteredNotes = allNotes;
|
||||
noteTags = extractedTags; // ✅ Update stored tags
|
||||
noteTags = extractedTags;
|
||||
_noteRelativePaths = extractedRelPaths;
|
||||
_noteContentLower = extractedContentLower;
|
||||
});
|
||||
|
||||
_filterNotes(); // ✅ Apply search immediately if a term is active
|
||||
|
|
@ -329,15 +337,12 @@ Happy note taking! ✨
|
|||
final normalizedSearch = searchText.replaceFirst(RegExp(r'^/'), '');
|
||||
|
||||
filteredNotes = notes.where((note) {
|
||||
final relativePath = path.relative(note.path, from: notesDirectoryPath!).toLowerCase();
|
||||
final noteTags = _extractTags(note.readAsStringSync());
|
||||
final relativePath = _noteRelativePaths[note] ?? '';
|
||||
final tags = noteTags[note] ?? {};
|
||||
final content = _includeFileContent ? (_noteContentLower[note] ?? '') : '';
|
||||
|
||||
// ✅ Read file content ONLY if `Include File Content` is enabled
|
||||
final content = _includeFileContent ? note.readAsStringSync().toLowerCase() : '';
|
||||
|
||||
// ✅ Match partial tags instead of requiring full matches (case-insensitive)
|
||||
final matchesTags = searchTags.every((tag) =>
|
||||
noteTags.any((noteTag) => noteTag.toLowerCase().startsWith(tag)));
|
||||
tags.any((noteTag) => noteTag.toLowerCase().startsWith(tag)));
|
||||
|
||||
if (!_includeFileContent) {
|
||||
return relativePath.contains(normalizedSearch) && matchesTags;
|
||||
|
|
@ -439,13 +444,17 @@ Happy note taking! ✨
|
|||
}
|
||||
|
||||
if (!newNote.existsSync()) {
|
||||
newNote.writeAsStringSync('# $safeTitle\n\n');
|
||||
final newContent = '# $safeTitle\n\n';
|
||||
newNote.writeAsStringSync(newContent);
|
||||
setState(() {
|
||||
notes.add(newNote);
|
||||
filteredNotes.add(newNote);
|
||||
noteTags[newNote] = {};
|
||||
_noteRelativePaths[newNote] = path.relative(newNote.path, from: notesDirectoryPath!).toLowerCase();
|
||||
_noteContentLower[newNote] = newContent.toLowerCase();
|
||||
});
|
||||
|
||||
Navigator.push(
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => NoteEditorScreen(
|
||||
|
|
@ -456,6 +465,7 @@ Happy note taking! ✨
|
|||
),
|
||||
),
|
||||
);
|
||||
if (context.mounted) _loadNotes();
|
||||
}
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue