推进本地索引与 AI changed-files 审计
- 为本地工作区补充 search/backlinks/tags/resource 引用索引与 watcher 单文件刷新 - 在页面设置中增加索引页签,并展示本地反链、标签和 AI changed-files 审计 - 补充本地搜索与本地 AI changed-files 浏览器 smoke,并回填当前优先级 checklist
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use crate::routes::{local_workspace_id_from_root_uri, refresh_local_search_index_for_path};
|
||||
use notify::event::ModifyKind;
|
||||
use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use serde_json::{json, Value};
|
||||
@@ -191,12 +192,20 @@ fn spawn_local_folder_watcher(
|
||||
continue;
|
||||
}
|
||||
for path in event.paths {
|
||||
if !is_markdown_path(&path) {
|
||||
if !is_local_search_index_path(&path) {
|
||||
continue;
|
||||
}
|
||||
let Some(relative_path) = relative_path_string(&canonical_root, &path) else {
|
||||
continue;
|
||||
};
|
||||
refresh_local_search_index_for_event(
|
||||
&canonical_root,
|
||||
&root_uri_for_task,
|
||||
&relative_path,
|
||||
);
|
||||
if !is_markdown_path(&path) {
|
||||
continue;
|
||||
}
|
||||
let payload = json!({
|
||||
"sourceKind": "local_folder",
|
||||
"rootUri": root_uri_for_task,
|
||||
@@ -215,6 +224,13 @@ fn spawn_local_folder_watcher(
|
||||
Ok((sender, shutdown_tx))
|
||||
}
|
||||
|
||||
fn refresh_local_search_index_for_event(root: &Path, root_uri: &str, relative_path: &str) {
|
||||
let Ok(workspace_id) = local_workspace_id_from_root_uri(root_uri) else {
|
||||
return;
|
||||
};
|
||||
let _ = refresh_local_search_index_for_path(root, root_uri, &workspace_id, relative_path);
|
||||
}
|
||||
|
||||
fn canonical_root_uri(root: &Path) -> String {
|
||||
format!("file://{}", root.display())
|
||||
}
|
||||
@@ -235,6 +251,29 @@ fn is_markdown_path(path: &Path) -> bool {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn is_local_search_index_path(path: &Path) -> bool {
|
||||
if is_markdown_path(path) {
|
||||
return true;
|
||||
}
|
||||
let file_name = path
|
||||
.file_name()
|
||||
.and_then(|value| value.to_str())
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
if file_name.ends_with(".mindmap.json") {
|
||||
return true;
|
||||
}
|
||||
let extension = path
|
||||
.extension()
|
||||
.and_then(|extension| extension.to_str())
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
matches!(
|
||||
extension.as_str(),
|
||||
"doc" | "docx" | "odt" | "ppt" | "pptx" | "odp" | "xls" | "xlsx" | "ods"
|
||||
)
|
||||
}
|
||||
|
||||
fn should_emit_event_kind(kind: &EventKind) -> bool {
|
||||
match kind {
|
||||
EventKind::Create(_) | EventKind::Remove(_) => true,
|
||||
@@ -276,7 +315,10 @@ fn system_time_ms(time: SystemTime) -> u128 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{should_emit_event_kind, LocalFolderWatcherRegistry};
|
||||
use super::{
|
||||
is_local_search_index_path, refresh_local_search_index_for_event, should_emit_event_kind,
|
||||
LocalFolderWatcherRegistry,
|
||||
};
|
||||
use notify::event::{AccessKind, CreateKind, DataChange, ModifyKind};
|
||||
use notify::EventKind;
|
||||
|
||||
@@ -348,4 +390,42 @@ mod tests {
|
||||
AccessKind::Read
|
||||
)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn watcher_index_path_filter_accepts_markdown_and_resources() {
|
||||
assert!(is_local_search_index_path(&std::path::Path::new(
|
||||
"docs/page.md"
|
||||
)));
|
||||
assert!(is_local_search_index_path(&std::path::Path::new(
|
||||
"maps/idea.mindmap.json"
|
||||
)));
|
||||
assert!(is_local_search_index_path(&std::path::Path::new(
|
||||
"office/report.xlsx"
|
||||
)));
|
||||
assert!(!is_local_search_index_path(&std::path::Path::new(
|
||||
"docs/image.png"
|
||||
)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn watcher_event_refreshes_local_search_index_path() {
|
||||
let root = test_root("search-index-event");
|
||||
std::fs::create_dir_all(root.join("docs")).expect("create docs");
|
||||
std::fs::write(root.join("README.md"), "# Home\n").expect("write home");
|
||||
std::fs::write(
|
||||
root.join("docs").join("watched.md"),
|
||||
"---\ntitle: Watched\n---\n# Watched\nWatcherToken\n",
|
||||
)
|
||||
.expect("write watched");
|
||||
|
||||
let root_uri = format!("file://{}", root.display());
|
||||
refresh_local_search_index_for_event(&root, &root_uri, "docs/watched.md");
|
||||
|
||||
let index_path = root.join(".mnote").join("index").join("search-index.json");
|
||||
let index = std::fs::read_to_string(&index_path).expect("index exists");
|
||||
assert!(index.contains("docs/watched.md"));
|
||||
assert!(index.contains("WatcherToken"));
|
||||
|
||||
let _ = std::fs::remove_dir_all(root);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user