Minga.Project.ProjectSearch (Minga v0.1.0)

Copy Markdown View Source

Searches across project files using ripgrep or grep.

Shells out to the fastest available tool and parses structured output into a flat list of match results. All functions are pure — no process state is mutated.

Tool preference

  1. rg (ripgrep) — preferred, fast, respects .gitignore, JSON output
  2. grep -rn — universally available fallback, slower, no column info

Summary

Types

A single search match across the project.

Search result.

Search strategy.

Functions

Detects which search strategy to use.

Parses a single grep output line (file:line:text) into a match map.

Parses a single ripgrep JSON line into a match map.

Searches for query in all files under root.

Types

match()

@type match() :: %{
  file: String.t(),
  line: pos_integer(),
  col: non_neg_integer(),
  text: String.t()
}

A single search match across the project.

result()

@type result() :: {:ok, [match()], truncated :: boolean()} | {:error, String.t()}

Search result.

strategy()

@type strategy() :: :rg | :grep | :none

Search strategy.

Functions

detect_strategy()

@spec detect_strategy() :: strategy()

Detects which search strategy to use.

parse_grep_line(line)

@spec parse_grep_line(String.t()) :: {:ok, match()} | :skip

Parses a single grep output line (file:line:text) into a match map.

Returns {:ok, match} for valid lines, :skip for unparseable lines.

Examples

iex> Minga.Project.ProjectSearch.parse_grep_line("lib/foo.ex:42:defmodule Foo")
{:ok, %{file: "lib/foo.ex", line: 42, col: 0, text: "defmodule Foo"}}

iex> Minga.Project.ProjectSearch.parse_grep_line("not a match")
:skip

parse_rg_json_line(line)

@spec parse_rg_json_line(String.t()) :: {:ok, match()} | :skip

Parses a single ripgrep JSON line into a match map.

Returns {:ok, match} for match lines, :skip for summary/context lines.

Examples

iex> json = ~s({"type":"match","data":{"path":{"text":"lib/foo.ex"},"lines":{"text":"defmodule Foo\n"},"line_number":1,"submatches":[{"match":{"text":"Foo"},"start":10,"end":13}]}})
iex> Minga.Project.ProjectSearch.parse_rg_json_line(json)
{:ok, %{file: "lib/foo.ex", line: 1, col: 10, text: "defmodule Foo"}}

search(query, root \\ File.cwd!())

@spec search(String.t(), String.t()) :: result()

Searches for query in all files under root.

Returns {:ok, matches, truncated?} on success where truncated? is true if results were capped at 10000.

Returns {:error, message} if no search tool is available or the query is empty.