diff --git a/CLAUDE.md b/CLAUDE.md index 1e768c2142..0f01e67c57 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,6 +58,8 @@ let { my_prop = $bindable(default_value) }: { my_prop?: string } = $props() - `refs "X" --caller` instead of reading files to find which function contains each reference - `callers "X"` / `callees "X"` for call-graph questions +EE files (`*_ee.rs`, `*_ee.ts`, `*_ee.svelte`) are indexed — you can `outline`, `def`, `body`, `refs` etc. on them just like regular files. + ```bash NAV="sh wm-ts-nav/nav" # Use --root backend for Rust, --root frontend/src for TS/Svelte diff --git a/wm-ts-nav/src/indexer.rs b/wm-ts-nav/src/indexer.rs index ef69fbd1f4..7b1da49a74 100644 --- a/wm-ts-nav/src/indexer.rs +++ b/wm-ts-nav/src/indexer.rs @@ -2,7 +2,7 @@ use anyhow::Result; use ignore::WalkBuilder; use rayon::prelude::*; use std::collections::HashSet; -use std::path::Path; +use std::path::{Path, PathBuf}; use crate::db::{self, Db}; use crate::parser::{self, Lang}; @@ -14,14 +14,40 @@ pub struct IndexStats { pub files_unchanged: usize, } +/// Find gitignored *_ee files (EE symlinks from windmill-ee-private). +fn find_ee_files(dir: &Path, out: &mut Vec) { + let Ok(entries) = std::fs::read_dir(dir) else { + return; + }; + for entry in entries.flatten() { + let path = entry.path(); + let name = entry.file_name(); + let name_str = name.to_str().unwrap_or(""); + // path.is_dir() follows symlinks + if path.is_dir() { + if !matches!(name_str, "target" | "node_modules" | ".git") { + find_ee_files(&path, out); + } + } else if Lang::from_path(&path).is_some() + && (name_str.ends_with("_ee.rs") + || name_str.ends_with("_ee.ts") + || name_str.ends_with("_ee.svelte")) + && path.exists() // follows symlink, checks target exists + { + out.push(path); + } + } +} + /// Incrementally update the index for the given root directory. /// Only re-parses files whose mtime has changed since last index. pub fn update_index(db: &Db, root: &Path) -> Result { // Collect all supported files using `ignore` crate (respects .gitignore) - let files: Vec<_> = WalkBuilder::new(root) + let mut files: Vec<_> = WalkBuilder::new(root) .hidden(true) .git_ignore(true) .git_global(false) + .follow_links(true) .build() .filter_map(|e| e.ok()) .filter(|e| e.file_type().map(|t| t.is_file()).unwrap_or(false)) @@ -29,6 +55,16 @@ pub fn update_index(db: &Db, root: &Path) -> Result { .map(|e| e.into_path()) .collect(); + // Also include gitignored *_ee files (EE symlinks from windmill-ee-private) + let file_set: HashSet<_> = files.iter().cloned().collect(); + let mut ee_files = Vec::new(); + find_ee_files(root, &mut ee_files); + for f in ee_files { + if !file_set.contains(&f) { + files.push(f); + } + } + let disk_paths: HashSet = files .iter() .map(|p| p.to_string_lossy().to_string()) diff --git a/wm-ts-nav/src/main.rs b/wm-ts-nav/src/main.rs index 8ae8c11f5b..a498b77c09 100644 --- a/wm-ts-nav/src/main.rs +++ b/wm-ts-nav/src/main.rs @@ -94,6 +94,13 @@ enum Command { }, } +/// Get absolute path without resolving symlinks (canonicalize the parent, keep the filename). +fn absolute_no_symlink(p: &std::path::Path) -> Result { + let parent = p.parent().unwrap_or(std::path::Path::new(".")); + let parent = std::fs::canonicalize(parent)?; + Ok(parent.join(p.file_name().unwrap_or_default())) +} + fn main() -> Result<()> { let cli = Cli::parse(); let root = cli @@ -114,7 +121,7 @@ fn main() -> Result<()> { ); } Command::Outline { file } => { - let file = std::fs::canonicalize(&file)?; + let file = absolute_no_symlink(&file)?; let symbols = db.file_symbols(&file.to_string_lossy())?; if symbols.is_empty() { println!("No symbols found");