Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ A command-line interface for searching Mozilla codebases using searchfox.org, wr

Also available as a Rust library (`searchfox-lib`) and Python package (`searchfox-py`).

Set `SEARCHFOX_BASE_URL` to point at a different server (e.g. a local mozsearch instance: `SEARCHFOX_BASE_URL=http://127.0.0.1:16995`); defaults to `https://searchfox.org`.

## Features

- Search across multiple Mozilla repositories (mozilla-central, beta, release, ESR branches, comm-central)
Expand Down
43 changes: 29 additions & 14 deletions searchfox-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::collections::HashMap;
#[command(
name = "searchfox-cli",
about = "Searchfox CLI for Mozilla code search",
long_about = "A command-line interface for searching Mozilla codebases using searchfox.org.\n\nExamples:\n searchfox-cli -q AudioStream\n searchfox-cli -q AudioStream -C -l 10\n searchfox-cli -q '^Audio.*' -r\n searchfox-cli -q AudioStream -p ^dom/media\n searchfox-cli -p PContent.ipdl # Search for files by path only\n searchfox-cli --get-file dom/media/AudioStream.h\n searchfox-cli --symbol AudioContext\n searchfox-cli --symbol 'AudioContext::CreateGain'\n searchfox-cli --id main\n searchfox-cli -q 'path:dom/media AudioStream'\n searchfox-cli -q 'symbol:AudioContext' --context 3\n searchfox-cli --define 'AudioContext::CreateGain'\n searchfox-cli --calls-from 'mozilla::dom::AudioContext::CreateGain' --depth 2\n searchfox-cli --calls-to 'mozilla::dom::AudioContext::CreateGain' --depth 3\n searchfox-cli --calls-between 'AudioContext,AudioNode' --depth 2\n searchfox-cli --field-layout 'mozilla::dom::AudioContext'"
long_about = "A command-line interface for searching Mozilla codebases using searchfox.org.\n\nExamples:\n searchfox-cli -q AudioStream\n searchfox-cli -q AudioStream -C -l 10\n searchfox-cli -q '^Audio.*' -r\n searchfox-cli -q AudioStream -p ^dom/media\n searchfox-cli -p PContent.ipdl # Search for files by path only\n searchfox-cli --get-file dom/media/AudioStream.h\n searchfox-cli --symbol AudioContext\n searchfox-cli --symbol 'AudioContext::CreateGain'\n searchfox-cli --id main\n searchfox-cli -q 'path:dom/media AudioStream'\n searchfox-cli -q 'symbol:AudioContext' --context 3\n searchfox-cli --define 'AudioContext::CreateGain'\n searchfox-cli --calls-from 'mozilla::dom::AudioContext::CreateGain' --depth 2\n searchfox-cli --calls-to 'mozilla::dom::AudioContext::CreateGain' --depth 3\n searchfox-cli --calls-between 'AudioContext,AudioNode' --depth 2\n searchfox-cli --field-layout 'mozilla::dom::AudioContext'\n\nEnvironment:\n SEARCHFOX_BASE_URL Override the server base URL (default: https://searchfox.org)"
)]
struct Args {
#[arg(short, long, help = "Search query string")]
Expand Down Expand Up @@ -365,7 +365,7 @@ async fn main() -> Result<()> {
if args.log_requests {
eprintln!("=== REQUEST LOGGING ENABLED ===");
if let Err(e) = client.ping().await {
eprintln!("[PING] Warning: Could not ping searchfox.org: {e}");
eprintln!("[PING] Warning: Could not ping {}: {e}", client.base_url());
}
eprintln!("================================");
}
Expand Down Expand Up @@ -445,7 +445,14 @@ async fn main() -> Result<()> {
if let Some((start, end)) = extract_line_range_from_output(&context) {
println!(
"{}",
generate_link(&client.repo, file_path, start, end, hash.as_deref())
generate_link(
client.base_url(),
&client.repo,
file_path,
start,
end,
hash.as_deref()
)
);
}
}
Expand Down Expand Up @@ -493,7 +500,14 @@ async fn main() -> Result<()> {
};
println!(
"{}",
generate_link(&client.repo, path, start, end, hash.as_deref())
generate_link(
client.base_url(),
&client.repo,
path,
start,
end,
hash.as_deref()
)
);
} else {
let content = client.get_file(path).await?;
Expand Down Expand Up @@ -641,8 +655,13 @@ async fn main() -> Result<()> {
println!("### {category}\n");
for r in group {
println!(
"- {}:{} — https://searchfox.org/{}/source/{}#{}",
r.path, r.line_number, args.repo, r.path, r.line_number
"- {}:{} — {}/{}/source/{}#{}",
r.path,
r.line_number,
client.base_url(),
args.repo,
r.path,
r.line_number
);
}
println!();
Expand All @@ -665,6 +684,7 @@ async fn main() -> Result<()> {
println!(
"{}",
generate_link(
client.base_url(),
&client.repo,
&result.path,
result.line_number,
Expand Down Expand Up @@ -746,6 +766,7 @@ async fn main() -> Result<()> {
}

fn generate_link(
base_url: &str,
repo: &str,
path: &str,
start_line: usize,
Expand All @@ -761,14 +782,8 @@ fn generate_link(
};
let rev_repo = searchfox_url_repo(repo);
match hash {
Some(h) => format!(
"https://searchfox.org/{}/rev/{}/{}{}",
rev_repo, h, path, fragment
),
None => format!(
"https://searchfox.org/{}/source/{}{}",
rev_repo, path, fragment
),
Some(h) => format!("{}/{}/rev/{}/{}{}", base_url, rev_repo, h, path, fragment),
None => format!("{}/{}/source/{}{}", base_url, rev_repo, path, fragment),
}
}

Expand Down
13 changes: 6 additions & 7 deletions searchfox-lib/src/blame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use std::collections::HashMap;
impl SearchfoxClient {
pub async fn get_head_hash(&self) -> anyhow::Result<String> {
let url = format!(
"https://searchfox.org/{}/commit-info/HEAD",
"{}/{}/commit-info/HEAD",
self.base_url,
searchfox_url_repo(&self.repo)
);
let response = self.get_raw(&url).await?;
Expand All @@ -31,7 +32,8 @@ impl SearchfoxClient {
.filter(|h| h.len() == 40 && h.chars().all(|c| c.is_ascii_hexdigit()))
{
let hgrev_url = format!(
"https://searchfox.org/{}/hgrev/{}",
"{}/{}/hgrev/{}",
self.base_url,
searchfox_url_repo(&self.repo),
hg_hash
);
Expand Down Expand Up @@ -66,7 +68,7 @@ impl SearchfoxClient {
lines: &[usize],
) -> Result<HashMap<usize, BlameInfo>> {
// Fetch the HTML page for the file
let url = format!("https://searchfox.org/{}/source/{}", self.repo, path);
let url = format!("{}/{}/source/{}", self.base_url, self.repo, path);
let html = self.get_html(&url).await?;

// Parse blame data from HTML
Expand Down Expand Up @@ -135,10 +137,7 @@ impl SearchfoxClient {

for chunk in revs.chunks(BATCH_SIZE) {
let revs_str = chunk.join(",");
let url = format!(
"https://searchfox.org/{}/commit-info/{}",
self.repo, revs_str
);
let url = format!("{}/{}/commit-info/{}", self.base_url, self.repo, revs_str);

let response = self.get_raw(&url).await?;
let mut commit_infos: Vec<CommitInfo> = serde_json::from_str(&response)?;
Expand Down
5 changes: 1 addition & 4 deletions searchfox-lib/src/call_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,7 @@ impl SearchfoxClient {
anyhow::bail!("No call graph query specified");
};

let mut url = Url::parse(&format!(
"https://searchfox.org/{}/query/default",
self.repo
))?;
let mut url = Url::parse(&format!("{}/{}/query/default", self.base_url, self.repo))?;
url.query_pairs_mut().append_pair("q", &query_string);

let response = self.get(url).await?;
Expand Down
2 changes: 1 addition & 1 deletion searchfox-lib/src/can_gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl SearchfoxClient {

async fn resolve_full_names(&self, symbol: &str) -> Result<Vec<String>> {
let query = format!("id:{symbol}");
let mut url = Url::parse(&format!("https://searchfox.org/{}/search", self.repo))?;
let mut url = Url::parse(&format!("{}/{}/search", self.base_url, self.repo))?;
url.query_pairs_mut().append_pair("q", &query);

let response = self.get(url).await?;
Expand Down
16 changes: 12 additions & 4 deletions searchfox-lib/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ impl SearchfoxClient {
crate::cache::prune(&conn);
std::sync::Mutex::new(conn)
});
let base_url = std::env::var("SEARCHFOX_BASE_URL")
.map(|v| v.trim_end_matches('/').to_string())
.unwrap_or_else(|_| "https://searchfox.org".to_string());
Ok(Self {
client,
repo,
log_requests,
base_url: "https://searchfox.org".to_string(),
base_url,
request_counter: std::sync::atomic::AtomicUsize::new(0),
cache,
cache_enabled: true,
Expand Down Expand Up @@ -133,15 +136,16 @@ impl SearchfoxClient {
}

eprintln!(
"[PING] Testing network latency to searchfox.org (ICMP ping disabled, using HTTP HEAD)..."
"[PING] Testing network latency to {} (ICMP ping disabled, using HTTP HEAD)...",
self.base_url
);

let ping_url = "https://searchfox.org/";
let ping_url = format!("{}/", self.base_url);
let start = Instant::now();

let response = self
.client
.head(ping_url)
.head(&ping_url)
.timeout(Duration::from_secs(10))
.send()
.await?;
Expand Down Expand Up @@ -266,6 +270,10 @@ impl SearchfoxClient {
&self.client
}

pub fn base_url(&self) -> &str {
&self.base_url
}

pub fn set_cache_enabled(&mut self, enabled: bool) {
self.cache_enabled = enabled;
}
Expand Down
5 changes: 1 addition & 4 deletions searchfox-lib/src/field_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,7 @@ impl SearchfoxClient {
pub async fn search_field_layout(&self, query: &FieldLayoutQuery) -> Result<serde_json::Value> {
let query_string = format!("field-layout:'{}'", query.class_name);

let mut url = Url::parse(&format!(
"https://searchfox.org/{}/query/default",
self.repo
))?;
let mut url = Url::parse(&format!("{}/{}/query/default", self.base_url, self.repo))?;
url.query_pairs_mut().append_pair("q", &query_string);

let response = self.get(url).await?;
Expand Down
4 changes: 2 additions & 2 deletions searchfox-lib/src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl SearchfoxClient {
pub async fn search(&self, options: &SearchOptions) -> Result<Vec<SearchResult>> {
let query = options.build_query();

let mut url = Url::parse(&format!("https://searchfox.org/{}/search", self.repo))?;
let mut url = Url::parse(&format!("{}/{}/search", self.base_url, self.repo))?;
url.query_pairs_mut()
.append_pair("q", &query)
.append_pair("case", if options.case { "true" } else { "false" })
Expand Down Expand Up @@ -334,7 +334,7 @@ impl SearchfoxClient {
symbol.to_string()
};
let query = format!("id:{search_symbol}");
let mut url = Url::parse(&format!("https://searchfox.org/{}/search", self.repo))?;
let mut url = Url::parse(&format!("{}/{}/search", self.base_url, self.repo))?;
url.query_pairs_mut().append_pair("q", &query);
if let Some(path) = path_filter {
url.query_pairs_mut().append_pair("path", path);
Expand Down
5 changes: 5 additions & 0 deletions searchfox-lib/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ pub fn is_potential_definition(line: &Line, query: &str) -> bool {
}

pub fn searchfox_url_repo(repo: &str) -> &str {
// The firefox-* renames only exist on searchfox.org; a server behind
// SEARCHFOX_BASE_URL serves trees under their configured names verbatim.
if std::env::var("SEARCHFOX_BASE_URL").is_ok() {
return repo;
}
match repo {
"mozilla-central" => "firefox-main",
"mozilla-beta" => "firefox-beta",
Expand Down
Loading