Dokumentation

Suchen

Die Suchbefehle von greppy ordnen sich nach einer Frage: Was weißt du über den Code, den du suchst?

Du kennst Befehl
was er tut, nicht seinen Namen greppy search "WAS ER TUT"
seinen Namen oder einen Teil davon greppy search-symbol NAME
wörtlichen Text, einen Config-Key, eine Meldung greppy search-pattern REGEX
eine Mischung, und du willst eine Rangliste greppy plus QUERY

Alle vier nutzen die Exit-Codes von grep: 0 bei einem Treffer, 1 bei keinem. Die Beispiele laufen auf serde 1.0.229.

search: nach Bedeutung

greppy search "convert a field name to snake case"
greppy search collect errors and report them at the end --kind method
greppy search "collect errors and report them at the end" --path serde_derive/ --code

search bettet deine Beschreibung mit EmbeddingGemma ein und gibt die nächstliegenden Definitionen als Rangliste zurück. Jede Zeile nennt Ort, Namen und einen kurzen Zweck-Hinweis. Alles nach dem Verb wird zu einer Anfrage zusammengefügt, Anführungszeichen sind also optional. Die Rangliste hat acht Treffer, auch mit --all.

$ greppy search "convert a field name to snake case"
serde_derive/src/internals/case.rs:82  RenameRule::apply_to_field — converts a string field into a case variant
serde_derive/src/internals/case.rs:174  rename_fields — tests that case field renames apply to their original values  test
serde_derive/src/internals/case.rs:57  RenameRule::apply_to_variant — converts a variant string to its canonical form
serde_derive/src/internals/case.rs:140  rename_variants — tests that apply_to_variant returns the expected name, type, and case for each variant  test
serde_derive/src/internals/case.rs:9  RenameRule
serde_derive/src/internals/attr.rs:133  unraw — wraps an Ident into a new Ident with a trimmed string
serde_derive/src/internals/case.rs:45  RenameRule::from_str — looks up a named rule in RENAME_RULES
test_suite/tests/test_identifier.rs:160  newtype_fallthrough — tests that a newtype enum falls through to its token  test

Die Anfrage nennt weder apply_to_field noch RenameRule. Der erste Treffer ist trotzdem die Methode, die eine Umbenennungsregel auf einen Feldnamen anwendet. --kind grenzt die Art der Definition ein:

$ greppy search collect errors and report them at the end --kind method
serde_derive/src/internals/ctxt.rs:62  Ctxt::drop — panics if errors are present and the thread is not panicking
serde_derive/src/internals/ctxt.rs:46  Ctxt::check — combines any pending errors into a single error
…
serde_derive/src/internals/ctxt.rs:41  Ctxt::syn_error — records a syn::Error into the errors collection
…

--json liefert zu jedem Treffer zusätzlich Signatur und Score, zum Beispiel "signature": "pub fn apply_to_field(self, field: &str) -> String" und "score": 0.5393914580345154.

Nimm die Treffer als Antwortkandidaten. Wähle den besten und antworte aus Symbol, Ort und Hinweis, die schon da sind, statt die Frage in eine weitere semantische Suche umzuformulieren. Meldet search keinen Treffer, ist ein search-pattern auf einen konkreten Begriff aus der Frage der nächste Schritt.

search findet auch Code, den der Graph nicht erreicht, etwa ein Reflection-Ziel oder einen generierten Handler, solange du beschreiben kannst, was er tut.

Während die Embeddings entstehen

Die Embeddings baut greppy nach dem Graphen im Hintergrund. Die erste semantische Abfrage in einem neuen Repository wartet, bis die einmaligen Embeddings veröffentlicht sind, und zeigt ihren Fortschritt auf stderr. Dann antwortet sie mit Exit-Code 0, ohne Teiltreffer.

$ greppy search "compress the ledger records"
semantic-search: semantic index building — 0/600 spans, ETA ~1m 15s (backend metal)
…
mod_05.py:300  compress_ledger_5_27 — compresses a list of items into a flat list of values
…

Die ausgelassenen stderr-Zeilen melden je Phase den Stand, etwa embedding: 256/600 spans; phase ETA about 20s, dazu PID, Worker-Zahl und den Befehl greppy --root DIR index status --json für Details.

Skripte warten, bis greppy index status --json mit 0 endet und sowohl "healthy": true als auch "embedding_complete": true meldet. Solange ein Build läuft, endet der Befehl mit 75.

search-symbol: nach Namen

greppy search-symbol rename
greppy search-symbol Rename --kind enum

search-symbol gibt die Definitionen zurück, deren Name NAME enthält, standardmäßig die ersten 20. Jede Zeile nennt die Art der Definition:

$ greppy search-symbol rename
test_suite/tests/ui/rename:1  serde.test_suite.tests.ui.rename  folder  test
serde_derive/src/internals/attr.rs:1386  get_renames  function
serde_derive/src/internals/case.rs:174  rename_fields  function  test
serde_derive/src/internals/attr.rs:925  Variant::rename_by_rules  method
…
$ greppy search-symbol Rename --kind enum
serde_derive/src/internals/case.rs:9  RenameRule  enum
test_suite/tests/test_annotations.rs:614  RenameEnum  enum  test
test_suite/tests/test_annotations.rs:630  RenameEnumSerializeDeserialize  enum  test

Ein Fehlgriff endet mit 1 und bietet trotzdem die beste Stufe naher Treffer an, unter einer Überschrift wie similar names: oder closest by meaning:. Die Kaskade probiert Varianten in Groß- und Kleinschreibung und mit Unterstrichen, Editierdistanz, Wortüberlappung und zuletzt die Bedeutung:

$ greppy search-symbol apply_to_snake_case
status: no_matches
scope: indexed definitions in the repository
message: no definition named `apply_to_snake_case`
next: search source text: greppy search-pattern apply_to_snake_case --fixed
next: refresh definitions after source changes: greppy index .

similar names:
serde_derive/src/internals/case.rs:23  RenameRule::SnakeCase  enum variant
serde_derive/src/internals/case.rs:82  RenameRule::apply_to_field  method
serde_derive/src/internals/case.rs:57  RenameRule::apply_to_variant  method
serde_derive/src/internals/case.rs:26  RenameRule::ScreamingSnakeCase  enum variant

Vor einem Navigationsbefehl brauchst du search-symbol nicht: who-calls, impact, callees, brief und path nehmen den Namen direkt an.

search-pattern: nach Text

greppy search-pattern 'TODO|FIXME'
greppy search-pattern 'deny_unknown_fields' --fixed
greppy search-pattern 'deny_unknown_fields' --fixed --path serde_derive/ --all

search-pattern findet jede Stelle, auf die der reguläre Ausdruck passt, auch in Kommentaren, Strings und Konfiguration, und nennt die Definition, in der jeder Treffer liegt. --fixed nimmt das Muster als wörtlichen Text.

$ greppy search-pattern 'TODO|FIXME'
serde_core/src/private/mod.rs:5
serde_derive/src/de/enum_externally.rs:63  serde_derive/src/de/enum_externally.rs::Function::deserialize
serde_derive/src/de/enum_untagged.rs:32  serde_derive/src/de/enum_untagged.rs::Function::deserialize
…
serde_derive/src/de/struct_.rs:296  serde_derive/src/de/struct_.rs::Function::deserialize_map
serde_derive/src/de/struct_.rs:592  serde_derive/src/de/struct_.rs::Function::deserialize_map_in_place

Ein Muster mit vielen Treffern druckt die Anzahl und eine Stichprobe von fünf Zeilen. --all druckt jeden Treffer, --code jede getroffene Zeile wörtlich. --kind ordnet einen Texttreffer der Definition zu, in der er liegt.

$ greppy search-pattern 'deny_unknown_fields' --fixed
— 41 matches in 11 files; showing 5

serde_derive/src/internals/symbol.rs:13  serde_derive/src/internals/symbol.rs::Variable::DENY_UNKNOWN_FIELDS
test_suite/tests/regression/issue2792.rs:6
test_suite/tests/test_de.rs:107
test_suite/tests/test_de_error.rs:31
test_suite/tests/test_de_error.rs:39

Verfehlt ein Muster, das wie ein regulärer Ausdruck aussieht, wegen --fixed, nennt greppy das Flag und den passenden nächsten Befehl, Exit-Code 1:

$ greppy search-pattern 'deny_unknown_fields\b' --fixed
status: no_matches
scope: live Greppy-discovered source files in the repository
message: no matches
next: search excluded source directly: greppy rg -n -F 'deny_unknown_fields\b' .
reason: --fixed took the pattern literally, and it reads as a regular expression
next: search it as a regular expression: greppy search-pattern 'deny_unknown_fields\b'
next: search definition names: greppy search-symbol 'deny_unknown_fields\b'

Eine Zeile innerhalb einer Definition trägt einen qualifizierten Locator, den greppy read annimmt. Die umschließende Definition liest du damit direkt:

$ greppy read serde_derive/src/internals/attr.rs::Container::deny_unknown_fields
serde_derive/src/internals/attr.rs:563-565  Container::deny_unknown_fields
    pub fn deny_unknown_fields(&self) -> bool {
        self.deny_unknown_fields
    }

plus: verschmolzene Rangliste

greppy plus "rename all fields" --k 5 --explain

plus kombiniert wörtliche und Volltext-Signale, Symbolnamen, unscharfe Bedeutung und Graph-Nachbarn zu einer Rangliste grep-artiger Zeilen datei:zeile:snippet. Das ist Suchausgabe, keine generierte Antwort. --k N setzt die Zahl der Treffer (Standard 10), --explain hängt an jede Zeile Score, Signale und Symbol an, --code druckt unter Treffern mit Symbol die umschließende Quelltext-Spanne:

$ greppy plus "rename all fields" --k 5 --explain
serde_derive/src/internals/symbol.rs:27:pub const RENAME_ALL_FIELDS: Symbol = Symbol("rename_all_fields");  # score=1.000 signals=fuzzy,fuzzy-token,symbol symbol=serde_derive/src/internals/symbol.rs::Variable::RENAME_ALL_FIELDS
test_suite/tests/test_macros.rs:782:fn test_rename_all_fields() {   # score=0.936 signals=fuzzy,fuzzy-token,kind=code,symbol,vector symbol=test_suite/tests/test_macros.rs::Function::test_rename_all_fields
serde_derive/src/internals/attr.rs:555:pub fn rename_all_fields_rules(&self) -> RenameAllRules {    # score=0.934 signals=fuzzy,fuzzy-token,kind=code,symbol,vector symbol=serde_derive/src/internals/attr.rs::Container::rename_all_fields_rules
serde_derive/src/internals/case.rs:174:fn rename_fields() { # score=0.873 signals=fuzzy,fuzzy-token,kind=code,vector symbol=serde_derive/src/internals/case.rs::Function::rename_fields
serde_derive/src/internals/attr.rs:940:pub fn rename_all_rules(&self) -> RenameAllRules {   # score=0.872 signals=fuzzy,fuzzy-token,kind=code,vector symbol=serde_derive/src/internals/attr.rs::Variant::rename_all_rules

Eine Anfrage aus einem Wort überspringt das semantische Signal und rankt über die exakten Signale. greppy sagt das auf stderr:

$ greppy plus deny_unknown_fields --k 3 --explain
grep: skipped EmbeddingGemma for literal/exact query; using exact signals only
serde_core/src/de/impls.rs:2124://     #[serde(deny_unknown_fields)]    # score=1.000 signals=text symbol=-
serde_core/src/de/impls.rs:2418://     #[serde(deny_unknown_fields)]    # score=1.000 signals=text symbol=-
serde_core/src/de/impls.rs:2598://     #[serde(deny_unknown_fields)]    # score=1.000 signals=text symbol=-

plus nimmt --k, --explain, --code und --json an. --kind, --path und --all gehören zu den anderen drei Suchbefehlen.

Gemeinsame Flags

Diese Flags gelten für search, search-symbol und search-pattern:

Flag Wirkung
--kind K Ergebnisse einer Art: function, method, class, struct, enum oder trait. Einen anderen Wert ignoriert greppy mit einem Hinweis auf die gültigen Werte
--path P Ergebnisse unterhalb dieser Datei oder dieses Verzeichnisses; wiederholbar
--code druckt zusätzlich den Quelltext an jedem Ergebnis
--all jedes Ergebnis statt der ersten; search bleibt bei acht Treffern
--json dieselbe Antwort als Daten, mit exakten Zählern

expand

Manche Ergebnisse bieten ein vorbereitetes Belegpaket an, etwa die letzte Zeile eines brief:

expand be76477a311cb85b — the call tree below expand_derive_deserialize sketched, 100 functions, 1186 lines

greppy expand ID druckt es in einem Aufruf, statt dass du jede Stelle datei:zeile einzeln öffnest. Mit --json steht das Angebot im Feld expand.

$ greppy expand be76477a311cb85b
serde_derive/src/de.rs:84
fn precondition(cx: &Ctxt, cont: &Container) {
  85  precondition_sized
  86  precondition_no_de_lifetime
}

serde_derive/src/de.rs:145
    fn new(cont: &Container) -> Self {
  147  this_type
  148  this_value
  149  borrowed_lifetimes
  150  build_generics
    }
…

Weiter zu read

greppy search-symbol apply_to_field --json | greppy read -
$ greppy search-symbol apply_to_field --json | greppy read -
serde_derive/src/internals/case.rs:81-109  RenameRule::apply_to_field
    /// Apply a renaming rule to a struct field, returning the version expected in the source.
    pub fn apply_to_field(self, field: &str) -> String {
        match self {
            None | LowerCase | SnakeCase => field.to_owned(),
            UpperCase => field.to_ascii_uppercase(),
…
            KebabCase => field.replace('_', "-"),
            ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"),
        }
    }