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
rg(ripgrep) — preferred, fast, respects.gitignore, JSON outputgrep -rn— universally available fallback, slower, no column info
Summary
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
@type match() :: %{ file: String.t(), line: pos_integer(), col: non_neg_integer(), text: String.t() }
A single search match across the project.
Search result.
@type strategy() :: :rg | :grep | :none
Search strategy.
Functions
@spec detect_strategy() :: strategy()
Detects which search strategy to use.
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
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"}}
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.