Dokumentation

Quelltext lesen

greppy liest nach Symbol oder nach Pfad. Ein Symbol-Read liefert die exakten Quelltext-Bytes der Definition. Ein Datei-Read liefert Zeilenbereiche, Seiten oder bei einer langen Quelldatei ihre Gliederung. Jeder Read druckt auf Wunsch ein Handle, das genau die gedruckte Spanne für einen späteren Edit festhält.

Befehl Liefert
greppy read S [S …] den Quelltext jeder Definition, Byte für Byte
greppy read-smart S [S …] den Quelltext, verschachtelte Blöcke zu einzeiligen Beschreibungen gefaltet
greppy read-file PATH [PATH …] Dateiinhalt: einen Zeilenbereich, alles oder Seiten zu 400 Zeilen; eine lange indexierte Quelldatei ohne Bereich zeigt ihre Gliederung

Die Beispiele laufen auf serde 1.0.229.

read

greppy read RenameRule::apply_to_variant
greppy read Ctxt::new Ctxt::check
greppy read deserialize_body --head 6        # die ersten 6 Zeilen
greppy read deserialize_body --tail 4        # die letzten 4 Zeilen
greppy read RenameRule::or --handle          # zusätzlich ein Edit-Handle

read druckt jede Definition als Quelltext-Bytes, mit der Kopfzeile datei:start-ende name. Dazu gehören die Dokumentation und die Attribute, die der Parser an die Definition hängt, etwa Rust-Kommentare /// und Attribute #[…] oder Python-Dekoratoren:

$ greppy read Ctxt::new Ctxt::check
serde_derive/src/internals/ctxt.rs:19-26  Ctxt::new
    /// Create a new context object.
    ///
    /// This object contains no errors, but will still trigger a panic if it is not `check`ed.
    pub fn new() -> Self {
        Ctxt {
            errors: RefCell::new(Some(Vec::new())),
        }
    }

serde_derive/src/internals/ctxt.rs:45-58  Ctxt::check
    /// Consume this object, producing a formatted error string if there are errors.
    pub fn check(self) -> syn::Result<()> {
        let mut errors = self.errors.borrow_mut().take().unwrap().into_iter();
…
        Err(combined)
    }
$ greppy read deserialize_in_place_body
serde_derive/src/de.rs:377-380  deserialize_in_place_body
#[cfg(not(feature = "deserialize_in_place"))]
fn deserialize_in_place_body(_cont: &Container, _params: &Parameters) -> Option<Stmts> {
    None
}

Einfache //-Kommentare über einer Funktion gehören nicht zur Definition, in Rust wie in Go. Über build_generics in serde_derive/src/ser.rs stehen zwei solche Zeilen, greppy read serde_derive/src/ser.rs::build_generics beginnt bei Zeile 135 mit der Signatur.

--head und --tail schneiden jede Definition zu, die Kopfzeile nennt den gedruckten Bereich:

$ greppy read deserialize_body --tail 4
serde_derive/src/de.rs:327-330  deserialize_body
            Data::Struct(_, _) => unreachable!("checked in serde_derive_internals"),
        }
    }
}

Einen mehrfach definierten Namen qualifizierst du mit seiner Datei. Die Mehrdeutigkeit nennt dir für jede Definition den passenden Befehl und endet mit Exit-Code 1:

$ greppy read from_ast
`from_ast` is 4 definitions
serde_derive/src/internals/ast.rs:62  serde_derive/src/internals/ast.rs::Container::from_ast — greppy read serde_derive/src/internals/ast.rs::Container::from_ast
serde_derive/src/internals/attr.rs:237  serde_derive/src/internals/attr.rs::Container::from_ast — greppy read serde_derive/src/internals/attr.rs::Container::from_ast
serde_derive/src/internals/attr.rs:748  serde_derive/src/internals/attr.rs::Variant::from_ast — greppy read serde_derive/src/internals/attr.rs::Variant::from_ast
serde_derive/src/internals/attr.rs:1015  serde_derive/src/internals/attr.rs::Field::from_ast — greppy read serde_derive/src/internals/attr.rs::Field::from_ast

--path wählt ebenfalls unter gleichnamigen Definitionen: greppy read from_ast --path serde_derive/src/internals/ast.rs liest Container::from_ast aus ast.rs.

read nimmt Symbole, keine Zeilenbereiche. Bekommt es einen Dateipfad, druckt es die Definitionen der Datei mit ihren qualifizierten Namen und die nächsten Befehle, nicht den Dateitext:

$ greppy read serde_derive/src/internals/ctxt.rs
`serde_derive/src/internals/ctxt.rs` is a file — read a symbol:
serde_derive/src/internals/ctxt.rs:12  serde_derive/src/internals/ctxt.rs::Class::Ctxt  class
serde_derive/src/internals/ctxt.rs:22  serde_derive/src/internals/ctxt.rs::Ctxt::new  method
serde_derive/src/internals/ctxt.rs:31  serde_derive/src/internals/ctxt.rs::Ctxt::error_spanned_by  method
serde_derive/src/internals/ctxt.rs:41  serde_derive/src/internals/ctxt.rs::Ctxt::syn_error  method
serde_derive/src/internals/ctxt.rs:46  serde_derive/src/internals/ctxt.rs::Ctxt::check  method
serde_derive/src/internals/ctxt.rs:62  serde_derive/src/internals/ctxt.rs::Ctxt::drop  method
read one: greppy read serde_derive/src/internals/ctxt.rs::Class::Ctxt · lines: greppy read-file serde_derive/src/internals/ctxt.rs --lines A:B

read-smart

greppy read-smart RenameRule::apply_to_variant
greppy read-smart RenameRule::apply_to_variant --depth 2

read-smart lässt Blöcke oberhalb der angegebenen Strukturtiefe roh und faltet die Blöcke auf dieser Tiefe zu einzeiligen Beschreibungen. Die Standardtiefe ist 1. Jede gefaltete Zeile nennt ihren Zeilenbereich und eine expand-ID für den verborgenen Block. So siehst du die Form einer langen Funktion, bevor du den Teil liest, den du brauchst:

$ greppy read-smart RenameRule::apply_to_variant
serde_derive/src/internals/case.rs:56-79  RenameRule::apply_to_variant
    /// Apply a renaming rule to an enum variant, returning the version expected in the source.
    pub fn apply_to_variant(self, variant: &str) -> String {
        … 58-78 Converts a variant into its canonical form. — greppy expand 55fb8674dc2a501a
    }
$ greppy read-smart RenameRule::apply_to_variant --depth 2
serde_derive/src/internals/case.rs:56-79  RenameRule::apply_to_variant
    /// Apply a renaming rule to an enum variant, returning the version expected in the source.
    pub fn apply_to_variant(self, variant: &str) -> String {
        match self {
            None | PascalCase => variant.to_owned(),
            LowerCase => variant.to_ascii_lowercase(),
            UpperCase => variant.to_ascii_uppercase(),
            CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
            … 63-72 Builds a snake case string from variant characters. — greppy expand 78207fd6ca1ef576
…

expand öffnet einen gefalteten Block wieder gefaltet, Ebene für Ebene:

$ greppy expand 78207fd6ca1ef576
            SnakeCase => {
                let mut snake = String::new();
                … 65-70 Converts a character sequence into lowercase snake case using variant characters. — greppy expand 63c6b28b84f5e000
                snake
            }

read-file

greppy read-file serde_derive/src/internals/case.rs --lines 112:118   # inklusiv, ab 1 gezählt
greppy read-file serde_derive/src/internals/case.rs --all             # jede Zeile
greppy read-file serde_derive/src/internals/case.rs                   # siehe unten

Für Pfade nimmst du read-file. Mit --lines oder --all druckt es genau diesen Text:

$ greppy read-file serde_derive/src/internals/case.rs --lines 112:118
serde_derive/src/internals/case.rs:112-118
    pub fn or(self, rule_b: Self) -> Self {
        match self {
            None => rule_b,
            _ => self,
        }
    }
}

Gibst du weder --lines noch --all an, gelten diese Regeln:

  • Eine Datei mit bis zu 60 Zeilen erscheint ganz. serde_derive/src/de/unit.rs hat 52 Zeilen und kommt als serde_derive/src/de/unit.rs:1-52.
  • Eine längere indexierte Quelldatei zeigt ihre Gliederung, dieselbe Definitionsliste mit den nächsten Befehlen wie greppy read PATH. --outline fordert sie ausdrücklich an.
  • Jede andere Datei erscheint in Seiten zu 400 Zeilen. Eine gekürzte Seite endet mit der Fortsetzung, für eine Datei mit 551 Zeilen etwa 151 more lines — greppy expand cb724cce764197c4 continues at 401.
$ greppy read-file serde_derive/src/internals/case.rs
Source outline for `serde_derive/src/internals/case.rs` — fingerprint-verified indexed definitions:
serde_derive/src/internals/case.rs:9  serde_derive/src/internals/case.rs::Enum::RenameRule  enum
serde_derive/src/internals/case.rs:45  serde_derive/src/internals/case.rs::RenameRule::from_str  method
serde_derive/src/internals/case.rs:57  serde_derive/src/internals/case.rs::RenameRule::apply_to_variant  method
serde_derive/src/internals/case.rs:82  serde_derive/src/internals/case.rs::RenameRule::apply_to_field  method
serde_derive/src/internals/case.rs:112  serde_derive/src/internals/case.rs::RenameRule::or  method
serde_derive/src/internals/case.rs:120  serde_derive/src/internals/case.rs::Class::ParseError  class
serde_derive/src/internals/case.rs:125  serde_derive/src/internals/case.rs::ParseError::fmt  method
serde_derive/src/internals/case.rs:140  serde_derive/src/internals/case.rs::Function::rename_variants  function
serde_derive/src/internals/case.rs:174  serde_derive/src/internals/case.rs::Function::rename_fields  function
read one: greppy read serde_derive/src/internals/case.rs::Enum::RenameRule · lines: greppy read-file serde_derive/src/internals/case.rs --lines A:B

Pfade dürfen relativ zum Repository oder absolut sein. Ein relativer Weg mit ../ bleibt im Repository eingeschlossen und endet außerhalb mit no such file. Ein ausdrücklich absoluter Pfad außerhalb, etwa ein heruntergeladenes Build-Artefakt, funktioniert.

Exakte Reads mit --lines und --all brauchen den Graph-Store nicht. Sie laufen also auch, während ein Repository indexiert wird.

Handles

--handle an read, read-smart oder read-file druckt zur gedruckten Spanne ein Handle. Ein Handle hält Datei, Byte-Bereich und Inhalts-Hashes fest. Es benennt genau die gedruckte Spanne: Bei gekürzter Ausgabe deckt es den gezeigten Teil ab.

$ greppy read RenameRule::or --handle
serde_derive/src/internals/case.rs:111-117  RenameRule::or
    /// Returns the `RenameRule` if it is not `None`, `rule_b` otherwise.
    pub fn or(self, rule_b: Self) -> Self {
        match self {
            None => rule_b,
            _ => self,
        }
    }
handle: geh2:AgpflQfNuU6DcfwlKqLlHpwi1QVrm02DIQ

Gefalteter Quelltext ist keine exakte Spanne. Für ihn druckt read-smart --handle statt eines Handles den Weg zu einem:

$ greppy read-smart RenameRule::or --handle
serde_derive/src/internals/case.rs:111-117  RenameRule::or
    /// Returns the `RenameRule` if it is not `None`, `rule_b` otherwise.
    pub fn or(self, rule_b: Self) -> Self {
        … 113-116 Returns rule_b when self is None, otherwise self. — greppy expand 9eafa1d9fda51840
    }
note: no edit handle for folded source; request greppy read SYMBOL --handle or read-file PATH --lines A:B --handle

Das Handle gibst du an replace-span, statt den Code noch einmal zu suchen:

greppy read RenameRule::or --handle
greppy replace-span H < or_if.rs

replace-span schreibt nichts, wenn sich die Datei seit dem Druck des Handles geändert hat. Ein Beispiel mit Quittung steht unter Transaktional editieren.

Aus einer Abfrage weiterlesen

Jeder Befehl nimmt seine Eingabe auch aus der Pipe, Such- und Navigationsergebnisse gehen direkt in einen Read:

greppy search-symbol apply_to_field --json | greppy read -
greppy callees visit_content_map --json | greppy read -
$ greppy callees visit_content_map --json | greppy read -
serde/src/private/de.rs:1608-1615  MapDeserializer::new
        fn new(content: Vec<(Content<'de>, Content<'de>)>) -> Self {
            MapDeserializer {
                iter: content.into_iter(),
                value: None,
                count: 0,
                error: PhantomData,
            }
        }

Ausgabe-Flags

Flag Wirkung
--handle druckt zu jeder gedruckten Spanne ein Edit-Handle
--path P Ergebnisse unterhalb dieser Datei oder dieses Verzeichnisses; wiederholbar. Bei read wählt es auch unter gleichnamigen Definitionen
--max-bytes N ein hartes Budget für die gedruckte Nutzlast
--root DIR liest aus einem anderen Repository

Ein Budget kürzt den Inhalt vor den Status- und Fortsetzungszeilen. Die Antwort sagt, wie es weitergeht:

$ greppy read RenameRule::apply_to_variant --max-bytes 300
truncated: true
total: 25
offset: 0
try: greppy read RenameRule::apply_to_variant --max-bytes 300 --offset 2
serde_derive/src/internals/case.rs:56-79  RenameRule::apply_to_variant
    /// Apply a renaming rule to an enum variant, returning the version expected in the source.

Frische

Reads liefern die aktuellen Bytes, nie eine veraltete Spanne. Passen die indexierten Spannen nicht mehr zur Datei, frischt greppy den Graphen auf oder bricht sicher ab und sagt, wann du es erneut versuchst. Während der Graph aufgefrischt wird, meldet read einen vorübergehenden Fehler, statt aktuelle Bytes mit einer alten Spanne zu paaren.