package engine import ( "fmt" "os" "os/exec" "path/filepath" "regexp" "strings" ) // SearchContent performs a full-text search using ripgrep func (jd *JadeDepo) SearchContent(query string) ([]string, error) { // Check if ripgrep is available if _, err := exec.LookPath("rg"); err != nil { return nil, fmt.Errorf("ripgrep (rg) is not installed. Please install it to use search functionality") } // Run ripgrep to search for content cmd := exec.Command("rg", "--files-with-matches", // Only show filenames "--glob", "*.md", // Only search markdown files "--glob", "!.jade", // Exclude .jade directory query, jd.Config.DepoPath, ) output, err := cmd.Output() if err != nil { // ripgrep returns exit code 1 when no matches found if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 { return []string{}, nil } return nil, fmt.Errorf("search failed: %w", err) } // Parse output into list of files files := strings.Split(strings.TrimSpace(string(output)), "\n") // Make paths relative to depo var results []string for _, file := range files { if file != "" { relPath, err := filepath.Rel(jd.Config.DepoPath, file) if err != nil { relPath = file } results = append(results, relPath) } } return results, nil } // SearchByTag searches for notes containing a specific tag func (jd *JadeDepo) SearchByTag(tag string) ([]string, error) { // Ensure tag has prefix if !strings.HasPrefix(tag, jd.Config.TagPrefix) { tag = jd.Config.TagPrefix + tag } // Use ripgrep to find the tag (escape special regex characters) escapedTag := regexp.QuoteMeta(tag) return jd.SearchContent(escapedTag) } // ListAllNotes returns all markdown notes in the depository func (jd *JadeDepo) ListAllNotes() ([]*Note, error) { var notes []*Note err := filepath.Walk(jd.Config.DepoPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // Skip .jade directory if info.IsDir() && info.Name() == ".jade" { return filepath.SkipDir } // Only process .md files if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") { relPath, err := filepath.Rel(jd.Config.DepoPath, path) if err != nil { return err } note, err := LoadNote(jd.Config.DepoPath, relPath, jd.Config.TagPrefix) if err != nil { // Log error but continue fmt.Fprintf(os.Stderr, "Warning: failed to load note %s: %v\n", relPath, err) return nil } notes = append(notes, note) } return nil }) if err != nil { return nil, fmt.Errorf("failed to list notes: %w", err) } return notes, nil } // FindNoteByTitle searches for a note by title (fuzzy match) func (jd *JadeDepo) FindNoteByTitle(title string) ([]*Note, error) { allNotes, err := jd.ListAllNotes() if err != nil { return nil, err } var matches []*Note titleLower := strings.ToLower(title) for _, note := range allNotes { // Check if title contains the search term if strings.Contains(strings.ToLower(note.Title), titleLower) { matches = append(matches, note) } } return matches, nil }