Files
CoderLambertandClaude Opus 4.7 d28a4141d4 docs: simplify release documentation
Simplify README to focus on quick start and essential usage.
Rename configuration.md to advanced.md for clearer positioning.
Add v0.1.2 changelog entry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:11:39 +08:00

9.5 KiB

Advanced Configuration

This document describes the main configuration options and rule schema for lazy-project-search.nvim.

Basic setup

require("project_search").setup({
  keymap = "<leader>sP",
  auto_init = true,
  storage_dir = vim.fn.stdpath("data") .. "/project-search/rules",
  template_dirs = {
    vim.fn.stdpath("config") .. "/project-search/templates",
    vim.fn.stdpath("data") .. "/project-search/templates",
  },
  root = nil,
  root_markers = {
    ".git",
    "package.json",
    "pnpm-workspace.yaml",
    "pnpm-lock.yaml",
    "yarn.lock",
    "package-lock.json",
    "lazy-lock.json",
    "stylua.toml",
    "selene.toml",
  },
  default_excludes = {
    ".git",
    "node_modules",
    "dist",
    "build",
    ".next",
    ".nuxt",
    "coverage",
  },
  templates = {
    common = true,
    react = true,
    vue = true,
    nest = true,
    user = {},
  },
  picker = {
    title = "Project Search",
    layout = "default",
  },
})

If you define keymaps through lazy.nvim, set:

keymap = false

This avoids registering the same key twice.

Options

keymap

Type:

string | false

Default:

"<leader>sP"

When set to a string, Project Search registers a normal-mode keymap for :ProjectSearch.

When set to false, no keymap is registered.

Recommended LazyVim usage:

opts = {
  keymap = false,
}

Then define the key through lazy.nvim:

keys = {
  {
    "<C-p>",
    "<cmd>ProjectSearch<cr>",
    desc = "Project Search",
  },
}

auto_init

Type:

boolean

Default:

true

When enabled, the first :ProjectSearch call creates a project rules file automatically if none exists.

storage_dir

Type:

string

Default:

vim.fn.stdpath("data") .. "/project-search/rules"

Directory used to store per-project rule files.

Rules are stored outside your source repository to avoid accidentally committing project-local search configuration.

template_dirs

Type:

string[]

Default:

{
  vim.fn.stdpath("config") .. "/project-search/templates",
  vim.fn.stdpath("data") .. "/project-search/templates",
}

Directories used to load user templates.

User templates are JSON files that can be enabled from:

templates = {
  user = { "my-template" },
}

or:

templates = {
  user = true,
}

When user = true, all user templates found in template_dirs are loaded.

root

Type:

string | fun(): string | nil

Default:

nil

Overrides project root detection.

Example:

root = function()
  return vim.fs.root(0, { ".git", "package.json" }) or vim.fn.getcwd()
end

root_markers

Type:

string[]

Used when root is not provided.

Project Search searches upward from the current working directory and buffer path to find one of these markers.

default_excludes

Type:

string[]

Default excludes applied to supported search runners.

Common defaults include:

{
  ".git",
  "node_modules",
  "dist",
  "build",
  ".next",
  ".nuxt",
  "coverage",
}

templates

Type:

{
  common = boolean,
  react = boolean,
  vue = boolean,
  nest = boolean,
  user = boolean | string[],
}

Controls which templates are used during rule initialization.

Example:

templates = {
  common = true,
  react = true,
  vue = false,
  nest = false,
  user = { "frontend-team" },
}

picker

Type:

{
  title = string,
  layout = string,
}

Controls the picker title and layout passed to Snacks Picker.

Rule file structure

A rule file looks like this:

{
  "version": 1,
  "meta": {
    "projectRoot": "/path/to/project",
    "template": "react",
    "createdAt": "2026-05-20T00:00:00Z",
    "note": "This file is stored outside your project. Edit presets to customize Project Search."
  },
  "presets": []
}

Only version and presets are required for validation.

Preset common fields

Every preset can use these fields:

{
  "id": "react.query_key",
  "name": "React: queryKey",
  "description": "Search TanStack Query queryKey usage.",
  "type": "grep",
  "group": "React",
  "tags": ["react", "query"],
  "order": 10,
  "enabled": true
}

id

Stable identifier for the rule.

If omitted, Project Search falls back to:

preset.<index>

Recommended format:

domain.feature

Examples:

react.query_key
react.hooks.kebab
project.service_files

name

Human-readable picker label.

description

Optional explanation shown in previews.

type

Required.

Allowed values:

files
grep
files_regex

group

Optional picker group name.

Examples:

Common
React
TanStack
Service
Router
Manage

tags

Optional string array for future filtering and organization.

Can be written as a string or string array:

"tags": "react"

or:

"tags": ["react", "query"]

order

Optional number used for sorting inside groups.

Lower values appear earlier.

enabled

Optional boolean.

Default:

true

When set to false, the preset is ignored.

files presets

Use files when you want to open a file picker rooted at a project directory.

{
  "id": "common.files.src",
  "name": "Files: src",
  "description": "Open a file picker rooted at src.",
  "type": "files",
  "group": "Common",
  "order": 10,
  "cwd": "src"
}

Fields:

cwd       optional directory used as picker cwd
dirs      optional fallback directory list
hidden    include hidden files
ignored   include ignored files

grep presets

Use grep when you want to search inside files.

{
  "id": "react.query_key",
  "name": "React: queryKey",
  "description": "Search TanStack Query queryKey usage.",
  "type": "grep",
  "group": "React",
  "order": 20,
  "search": "queryKey",
  "dirs": ["src", "app"],
  "glob": ["*.ts", "*.tsx"]
}

Regex grep:

{
  "id": "react.query_hooks",
  "name": "TanStack Query: hooks",
  "description": "Search query-related React hooks.",
  "type": "grep",
  "group": "TanStack",
  "order": 10,
  "regex": true,
  "search": "useQuery|useMutation|useInfiniteQuery",
  "dirs": ["src"],
  "glob": ["*.ts", "*.tsx"]
}

Live grep:

{
  "id": "project.service_content",
  "name": "Search: service content",
  "description": "Live grep under src/service.",
  "type": "grep",
  "group": "Service",
  "live": true,
  "regex": true,
  "search": "",
  "dirs": ["src/service"],
  "glob": ["*.ts", "*.tsx"]
}

Fields:

search    search query
regex     boolean, default false
live      boolean, default false
dirs      directory or directory list
glob      glob or glob list
exclude   exclude pattern or pattern list
args      extra rg args
hidden    include hidden files
ignored   include ignored files

files_regex presets

Use files_regex when you want to find files by path convention.

It is backed by fd / fdfind.

{
  "id": "react.hooks.kebab",
  "name": "Hooks: use-* kebab files",
  "description": "Find hook files such as hooks/use-claw-history-panel.ts.",
  "type": "files_regex",
  "group": "React",
  "order": 10,
  "regex": "(^|/)hooks/use-[a-z0-9-]+\\.(ts|tsx|js|jsx)$",
  "dirs": ["src", "app", "packages"],
  "exclude": ["node_modules", "dist", "build", ".next"]
}

Fields:

regex     required fd-compatible regex string
dirs      directory or directory list
exclude   exclude pattern or pattern list
hidden    include hidden files
ignored   include ignored files

Important: fd uses Rust regex syntax. Look-around is not supported.

Avoid:

{
  "regex": "service/[^/]+(?<!\\.test|\\.spec)\\.(ts|tsx)$"
}

Prefer:

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

Validation

Validate the current project rules from Neovim:

:ProjectSearch validate

Run repository checks locally:

make check

make check runs:

format-check
validate
test
test-runner
{
  "version": 1,
  "presets": [
    {
      "id": "react.files.src",
      "name": "Files: src",
      "description": "Open files under src.",
      "type": "files",
      "group": "Common",
      "order": 10,
      "cwd": "src"
    },
    {
      "id": "react.class_name",
      "name": "React: className",
      "description": "Search JSX className usage.",
      "type": "grep",
      "group": "React",
      "order": 20,
      "search": "className",
      "dirs": ["src", "app"],
      "glob": ["*.tsx", "*.jsx"]
    },
    {
      "id": "react.query_key",
      "name": "React: queryKey",
      "description": "Search TanStack Query queryKey usage.",
      "type": "grep",
      "group": "TanStack",
      "order": 10,
      "search": "queryKey",
      "dirs": ["src", "app"],
      "glob": ["*.ts", "*.tsx"]
    },
    {
      "id": "react.hooks.kebab",
      "name": "Hooks: use-* kebab files",
      "description": "Find hook files such as hooks/use-claw-history-panel.ts.",
      "type": "files_regex",
      "group": "React",
      "order": 30,
      "regex": "(^|/)hooks/use-[a-z0-9-]+\\.(ts|tsx|js|jsx)$",
      "dirs": ["src", "app", "packages"]
    },
    {
      "id": "project.service_files",
      "name": "Service: API layer files",
      "description": "Find service files while excluding tests.",
      "type": "files_regex",
      "group": "Service",
      "order": 10,
      "regex": "service/[^/]+\\.(ts|tsx)$",
      "dirs": ["src"],
      "exclude": ["*.test.ts", "*.test.tsx", "*.spec.ts", "*.spec.tsx"]
    }
  ]
}