Files
lazy-project-search.nvim/prd/v2.md
T
lambert.liandClaude Opus 4.7 92d8a93a68 feat: extract templates to JSON and expand preset coverage
- Extract preset definitions from templates.lua into per-framework
  JSON files under lua/project_search/templates/ (common, react, vue, nest)
- templates.lua now loads from JSON via M.load(name), keeping the
  default_rules() API unchanged
- Expand common: add console.log, deprecated, live search presets
- Expand React: add React Query hooks, context providers, Zustand
  stores, shadcn imports, service files/content presets
- Expand Vue: add watch/watchEffect, Pinia stores, provide/inject,
  composables, page components presets
- Expand NestJS: add DTOs, guards, interceptors, decorators,
  entity/module files presets
- Split preset guide into separate en/ and zh/ directories

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 16:55:14 +08:00

8.5 KiB

lazy-project-search.nvim Implementation Notes And Roadmap

This document tracks the current implementation and future roadmap. Historical single-file migration notes have been removed because the plugin architecture is now complete.

1. Current Architecture

lua/project_search/
├── init.lua
├── config.lua
├── util.lua
├── detector.lua
├── storage.lua
├── templates.lua
├── runner.lua
├── picker.lua
├── commands.lua
└── health.lua
Module Responsibility
init.lua Public plugin API and setup()
config.lua Defaults and user options
util.lua Root detection, path helpers, JSON helpers, command helpers
detector.lua React / Vue / Nest project detection
storage.lua Rule path, load/save/init/reset/edit
templates.lua Built-in common / React / Vue / Nest presets
runner.lua Execute files, grep, and files_regex
picker.lua Main Snacks Picker UI, formatting, preview
commands.lua User command registration
health.lua :checkhealth project_search implementation

2. Implemented Features

Feature Status Notes
Standard plugin shape Done Uses lua/project_search/init.lua as main
Lazy loading Done Recommended via lazy.nvim cmd and keys
Configurable keymap Done Internal keymap still supported, but lazy.nvim keys is recommended
Recommended keymap Done <C-p> in normal mode
Plain Neovim support Done Requires snacks.nvim picker; root fallback uses markers
External project rule storage Done stdpath("data")/project-search/rules
Auto init Done Opens generated JSON on first use
Rule editing Done :ProjectSearchEdit
Rule reset Done :ProjectSearchReset / :ProjectSearchInit!
Rule path copy Done Manage action in picker
Rule previews Done Generated on demand
Search/manage grouping Done Search rules first, manage actions at bottom
files runner Done Uses Snacks files picker
grep runner Done Uses Snacks grep picker, supports exclude
files_regex runner Done Uses fd / fdfind
files_regex error reporting Done Reports fd stderr for invalid regex
Template detection Done common / React / Vue / Nest
Health check Done Checks Snacks, fd/fdfind, rg, rules path
English docs Done README.md
Chinese docs Done README.zh-CN.md
Example screenshots Done docs/assets/

3. Performance Decisions

Startup

The plugin should not eagerly load picker/storage/templates during Neovim startup.

Current strategy:

  • lazy.nvim registers command and key triggers
  • setup() registers lightweight commands only
  • heavy modules are required on command execution
  • recommended user config sets keymap = false

Root Detection

The plugin supports both LazyVim and plain Neovim. Root detection priority is:

  1. opts.root function or string
  2. LazyVim.root() when LazyVim is available
  3. vim.fs.root(source, opts.root_markers)
  4. current working directory fallback

This avoids hard-coding a LazyVim dependency while preserving LazyVim's project-root behavior.

Picker Open Latency

Earlier versions generated every preview before showing the picker. That caused visible latency when many presets existed.

Current strategy:

  • picker items are lightweight
  • Markdown previews are generated only when Snacks requests the current item preview
  • JSON pretty printing is implemented in Lua, avoiding repeated external process startup
  • matcher.sort_empty = false preserves grouping order

4. Keymap Policy

Recommended:

<C-p>

Reasoning:

  • fast single chord
  • mnemonic for Project Search / Project Picker
  • does not conflict with LazyVim's common <leader>p Yank History mapping
  • does not affect insert-mode completion navigation

Fallbacks:

Keymap Notes
<leader>sP conservative LazyVim search namespace fallback
<leader>fP file/find namespace fallback

Avoid:

Keymap Reason
<leader>p commonly Yank History
<leader><space> LazyVim Find Files
<leader>fp LazyVim Projects
<leader>sp LazyVim Search Plugin Spec

5. Rule Semantics

files

Input:

{
  "type": "files",
  "cwd": "src"
}

Execution:

  • resolve cwd relative to project root
  • open Snacks.picker.files

grep

Input:

{
  "type": "grep",
  "search": "queryKey",
  "dirs": ["src"],
  "glob": ["*.ts", "*.tsx"]
}

Execution:

  • expand dirs to absolute paths
  • merge global default excludes and preset excludes
  • convert exclude patterns to ripgrep --glob '!...' args
  • open Snacks.picker.grep
  • pass through regex, live, glob, args, hidden, and ignored

The absolute-dir expansion is important because Snacks grep treats configured dirs as explicit search paths.

files_regex

Input:

{
  "type": "files_regex",
  "regex": "service/[^/]+\\.(ts|tsx)$",
  "dirs": ["src"],
  "exclude": ["*.test.ts", "*.spec.ts"]
}

Execution:

  • use fd --type f --full-path
  • merge global default excludes and preset excludes
  • expand dirs relative to project root
  • report fd stderr when regex parsing fails
  • open custom file picker from matched items

Important:

fd uses Rust regex and does not support look-around.
Negative filters should use exclude instead.

6. Current Template Coverage

common

  • Files: src
  • Search: TODO / FIXME
  • Search: env usage
  • Search: console.log
  • Search: deprecated
  • Search: live in src

react

  • Hooks: use-* kebab files
  • Hooks: claw hooks
  • Hooks: panel hooks
  • React: className
  • React: queryKey
  • React: custom hook definitions
  • React Query: hooks
  • React: context providers
  • Zustand: store definitions
  • UI: shadcn component imports
  • Service: API layer files
  • Search: service content

vue

  • Vue: defineProps
  • Vue: defineEmits
  • Vue: ref / computed
  • Vue: watch / watchEffect
  • Pinia: store definitions
  • Vue: provide / inject
  • Composables: use-* files
  • Vue: page components

nest

  • Nest: Controllers
  • Nest: Services
  • Nest: Modules
  • Nest: DTOs
  • Nest: Guards
  • Nest: Interceptors
  • Nest: custom decorators
  • Nest: entity files
  • Nest: module files

7. Documentation Status

Current docs:

README.md
README.zh-CN.md
CLAUDE.md
docs/rule/en/PRESET_GUIDE.md
docs/rule/zh/PRESET_GUIDE.md
docs/rule/en/react-rule.json
docs/assets/react-project-search.png
docs/assets/react-service-dir-search.png

Docs cover:

  • product pain point
  • LazyVim install
  • plain Neovim install
  • root detection and root_markers
  • keymap recommendations
  • how to adapt rules to a project
  • rule types
  • React examples
  • files_regex limitations
  • troubleshooting

8. Roadmap

v0.3 Rule Authoring

Feature Reason
Rule variables Avoid repeating dirs and glob lists
Rule inheritance Allow generated templates plus project-specific additions
Rule enable/disable flag Let users keep experimental rules without deleting them
Rule validation command Validate JSON schema, unsupported regex, missing dirs

Example variable design:

{
  "vars": {
    "sourceDirs": ["src", "app", "packages"],
    "tsFiles": ["*.ts", "*.tsx"]
  },
  "presets": [
    {
      "name": "React: queryKey",
      "type": "grep",
      "search": "queryKey",
      "dirs": "$sourceDirs",
      "glob": "$tsFiles"
    }
  ]
}

v0.4 Project Index

Maintain:

~/.local/share/nvim/project-search/index.json

Potential command:

:ProjectSearchProjects

Use cases:

  • list initialized projects
  • jump to a project's rule file
  • delete stale rules
  • inspect last-used timestamp

v0.5 Rule Management UI

Potential picker actions:

Enter      run rule
Ctrl-e     edit rules file
Ctrl-r     reset rules
Ctrl-y     copy current rule JSON
Ctrl-d     disable selected rule

v0.6 Save Current Search As Rule

Potential commands:

:ProjectSearchSaveGrep
:ProjectSearchSaveFilesRegex

Goal:

manual search -> name it -> save into current project JSON

9. Non-Goals For Now

  • No repository-local rules by default, because avoiding source pollution is a core product decision.
  • No dependency on jq; picker previews should not spawn external formatters.
  • No mandatory Telescope support; Snacks Picker is the target UI.
  • No project index until rule-file workflows are stable.