Read source
greppy reads by symbol or by path. A symbol read returns the definition's exact source bytes. A file read returns line ranges, pages or, for a long source file, its outline. Any read can also print a handle that pins exactly the printed span for a later edit.
| Command | Returns |
|---|---|
greppy read S [S …] |
the source of each definition, byte-exact |
greppy read-smart S [S …] |
the source with nested blocks folded into one-line descriptions |
greppy read-file PATH [PATH …] |
file content: a line range, everything, or 400-line pages; a long indexed source file without a range shows its outline |
The examples run on serde 1.0.229.
read
greppy read RenameRule::apply_to_variant
greppy read Ctxt::new Ctxt::check
greppy read deserialize_body --head 6 # the first 6 lines
greppy read deserialize_body --tail 4 # the last 4 lines
greppy read RenameRule::or --handle # plus an edit handle
read prints each definition as source bytes, with the header line file:start-end name. This includes the documentation and attributes the parser attaches to the definition, such as Rust /// comments and #[…] attributes or Python decorators:
$ 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
}
Plain // comments above a function are not part of the definition, in Rust and in Go alike. Two such lines sit above build_generics in serde_derive/src/ser.rs, and greppy read serde_derive/src/ser.rs::build_generics starts at line 135 with the signature.
--head and --tail trim each definition, and the header line names the printed range:
$ greppy read deserialize_body --tail 4
serde_derive/src/de.rs:327-330 deserialize_body
Data::Struct(_, _) => unreachable!("checked in serde_derive_internals"),
}
}
}
You qualify a name defined more than once with its file. The ambiguity response gives you the matching command for each definition and ends with 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 also picks among same-named definitions: greppy read from_ast --path serde_derive/src/internals/ast.rs reads Container::from_ast from ast.rs.
read takes symbols, not line ranges. Given a file path, it prints the file's definitions with their qualified names and the next commands, not the file text:
$ 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 keeps blocks above the given structural depth raw and folds the blocks at that depth into one-line descriptions. The default depth is 1. Each folded line names its line range and an expand ID for the hidden block. This shows you the shape of a long function before you read the part you need:
$ 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 opens a folded block folded again, one level at a time:
$ 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 # inclusive, 1-based
greppy read-file serde_derive/src/internals/case.rs --all # every line
greppy read-file serde_derive/src/internals/case.rs # see below
You use read-file for paths. With --lines or --all it prints exactly that 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,
}
}
}
These rules apply when you pass neither --lines nor --all:
- A file of up to 60 lines prints whole.
serde_derive/src/de/unit.rshas 52 lines and comes back asserde_derive/src/de/unit.rs:1-52. - A longer indexed source file shows its outline, the same definition list with next commands as
greppy read PATH.--outlineasks for it explicitly. - Any other file prints in 400-line pages. A cut page ends with how to continue, for a file of 551 lines for example
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
Paths can be repository-relative or absolute. A relative path with ../ stays confined to the repository and ends with no such file outside it. An explicitly absolute path outside it, such as a downloaded build artifact, works.
Exact reads with --lines and --all do not need the graph store. They keep working while a repository is being indexed.
Handles
--handle on read, read-smart or read-file prints a handle for the printed span. A handle pins the file, the byte range and the content hashes. It names exactly the printed span: when the output is cut, it covers the shown part.
$ 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
Folded source is not an exact span. For it, read-smart --handle prints the way to a handle instead of a handle:
$ 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
You pass the handle to replace-span instead of locating the code again:
greppy read RenameRule::or --handle
greppy replace-span H < or_if.rs
replace-span writes nothing if the file changed since the handle was printed. You find an example with a receipt under Edit transactionally.
Chain from a query
Every command also takes its input from the pipe, so search and navigation results go straight into a 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,
}
}
Output flags
| Flag | Effect |
|---|---|
--handle |
prints an edit handle for each printed span |
--path P |
results under that file or directory; repeatable. For read, it also picks among same-named definitions |
--max-bytes N |
a hard budget for the printed payload |
--root DIR |
reads from another repository |
A budget cuts the content before the status and continuation lines. The response tells you how to continue:
$ 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.
Freshness
Reads return current bytes, never a stale span. When the indexed spans no longer match the file, greppy refreshes the graph or fails closed and tells you when to retry. While the graph is refreshing, read reports a temporary failure instead of pairing current bytes with an old span.